1 // Copyright 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 <string>
6
7 #include "base/command_line.h"
8 #include "base/metrics/histogram_samples.h"
9 #include "base/metrics/statistics_recorder.h"
10 #include "base/strings/stringprintf.h"
11 #include "chrome/browser/chrome_notification_types.h"
12 #include "chrome/browser/infobars/infobar_service.h"
13 #include "chrome/browser/password_manager/chrome_password_manager_client.h"
14 #include "chrome/browser/password_manager/password_store_factory.h"
15 #include "chrome/browser/password_manager/test_password_store_service.h"
16 #include "chrome/browser/ui/browser.h"
17 #include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h"
18 #include "chrome/browser/ui/tabs/tab_strip_model.h"
19 #include "chrome/common/chrome_version_info.h"
20 #include "chrome/test/base/in_process_browser_test.h"
21 #include "chrome/test/base/test_switches.h"
22 #include "chrome/test/base/ui_test_utils.h"
23 #include "components/autofill/core/browser/autofill_test_utils.h"
24 #include "components/infobars/core/confirm_infobar_delegate.h"
25 #include "components/infobars/core/infobar.h"
26 #include "components/infobars/core/infobar_manager.h"
27 #include "components/password_manager/core/browser/test_password_store.h"
28 #include "components/password_manager/core/common/password_manager_switches.h"
29 #include "content/public/browser/render_view_host.h"
30 #include "content/public/browser/web_contents.h"
31 #include "content/public/browser/web_contents_observer.h"
32 #include "content/public/test/browser_test_utils.h"
33 #include "content/public/test/test_utils.h"
34 #include "net/test/embedded_test_server/embedded_test_server.h"
35 #include "net/url_request/test_url_fetcher_factory.h"
36 #include "testing/gmock/include/gmock/gmock.h"
37 #include "third_party/WebKit/public/web/WebInputEvent.h"
38 #include "ui/events/keycodes/keyboard_codes.h"
39 #include "ui/gfx/geometry/point.h"
40
41
42 // NavigationObserver ---------------------------------------------------------
43
44 namespace {
45
46 // Observer that waits for navigation to complete and for the password infobar
47 // to be shown.
48 class NavigationObserver : public content::WebContentsObserver,
49 public infobars::InfoBarManager::Observer {
50 public:
NavigationObserver(content::WebContents * web_contents)51 explicit NavigationObserver(content::WebContents* web_contents)
52 : content::WebContentsObserver(web_contents),
53 message_loop_runner_(new content::MessageLoopRunner),
54 infobar_shown_(false),
55 infobar_removed_(false),
56 should_automatically_accept_infobar_(true),
57 infobar_service_(InfoBarService::FromWebContents(web_contents)) {
58 infobar_service_->AddObserver(this);
59 }
60
~NavigationObserver()61 virtual ~NavigationObserver() {
62 if (infobar_service_)
63 infobar_service_->RemoveObserver(this);
64 }
65
66 // Normally Wait() will not return until a main frame navigation occurs.
67 // If a path is set, Wait() will return after this path has been seen,
68 // regardless of the frame that navigated. Useful for multi-frame pages.
SetPathToWaitFor(const std::string & path)69 void SetPathToWaitFor(const std::string& path) {
70 wait_for_path_ = path;
71 }
72
73 // content::WebContentsObserver:
DidFinishLoad(int64 frame_id,const GURL & validated_url,bool is_main_frame,content::RenderViewHost * render_view_host)74 virtual void DidFinishLoad(
75 int64 frame_id,
76 const GURL& validated_url,
77 bool is_main_frame,
78 content::RenderViewHost* render_view_host) OVERRIDE {
79 if (!wait_for_path_.empty()) {
80 if (validated_url.path() == wait_for_path_)
81 message_loop_runner_->Quit();
82 } else if (is_main_frame) {
83 message_loop_runner_->Quit();
84 }
85 }
86
infobar_shown() const87 bool infobar_shown() const { return infobar_shown_; }
infobar_removed() const88 bool infobar_removed() const { return infobar_removed_; }
89
disable_should_automatically_accept_infobar()90 void disable_should_automatically_accept_infobar() {
91 should_automatically_accept_infobar_ = false;
92 }
93
Wait()94 void Wait() {
95 message_loop_runner_->Run();
96 }
97
98 private:
99 // infobars::InfoBarManager::Observer:
OnInfoBarAdded(infobars::InfoBar * infobar)100 virtual void OnInfoBarAdded(infobars::InfoBar* infobar) OVERRIDE {
101 if (should_automatically_accept_infobar_) {
102 infobar_service_->infobar_at(0)->delegate()->
103 AsConfirmInfoBarDelegate()->Accept();
104 }
105 infobar_shown_ = true;
106 }
107
OnInfoBarRemoved(infobars::InfoBar * infobar,bool animate)108 virtual void OnInfoBarRemoved(infobars::InfoBar* infobar,
109 bool animate) OVERRIDE {
110 infobar_removed_ = true;
111 }
112
OnManagerShuttingDown(infobars::InfoBarManager * manager)113 virtual void OnManagerShuttingDown(
114 infobars::InfoBarManager* manager) OVERRIDE {
115 ASSERT_EQ(infobar_service_, manager);
116 infobar_service_->RemoveObserver(this);
117 infobar_service_ = NULL;
118 }
119
120 std::string wait_for_path_;
121 scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
122 bool infobar_shown_;
123 bool infobar_removed_;
124 // If |should_automatically_accept_infobar_| is true, then whenever the test
125 // sees an infobar added, it will click its accepting button. Default = true.
126 bool should_automatically_accept_infobar_;
127 InfoBarService* infobar_service_;
128
129 DISALLOW_COPY_AND_ASSIGN(NavigationObserver);
130 };
131
132 } // namespace
133
134
135 // PasswordManagerBrowserTest -------------------------------------------------
136
137 class PasswordManagerBrowserTest : public InProcessBrowserTest {
138 public:
PasswordManagerBrowserTest()139 PasswordManagerBrowserTest() {}
~PasswordManagerBrowserTest()140 virtual ~PasswordManagerBrowserTest() {}
141
142 // InProcessBrowserTest:
SetUpOnMainThread()143 virtual void SetUpOnMainThread() OVERRIDE {
144 // Use TestPasswordStore to remove a possible race. Normally the
145 // PasswordStore does its database manipulation on the DB thread, which
146 // creates a possible race during navigation. Specifically the
147 // PasswordManager will ignore any forms in a page if the load from the
148 // PasswordStore has not completed.
149 PasswordStoreFactory::GetInstance()->SetTestingFactory(
150 browser()->profile(), TestPasswordStoreService::Build);
151 }
152
153 protected:
WebContents()154 content::WebContents* WebContents() {
155 return browser()->tab_strip_model()->GetActiveWebContents();
156 }
157
RenderViewHost()158 content::RenderViewHost* RenderViewHost() {
159 return WebContents()->GetRenderViewHost();
160 }
161
controller()162 ManagePasswordsUIController* controller() {
163 return ManagePasswordsUIController::FromWebContents(WebContents());
164 }
165
166 // Wrapper around ui_test_utils::NavigateToURL that waits until
167 // DidFinishLoad() fires. Normally this function returns after
168 // DidStopLoading(), which caused flakiness as the NavigationObserver
169 // would sometimes see the DidFinishLoad event from a previous navigation and
170 // return immediately.
NavigateToFile(const std::string & path)171 void NavigateToFile(const std::string& path) {
172 if (!embedded_test_server()->Started())
173 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
174
175 ASSERT_FALSE(CommandLine::ForCurrentProcess()->HasSwitch(
176 password_manager::switches::kEnableAutomaticPasswordSaving));
177 NavigationObserver observer(WebContents());
178 GURL url = embedded_test_server()->GetURL(path);
179 ui_test_utils::NavigateToURL(browser(), url);
180 observer.Wait();
181 }
182
183 // Waits until the "value" attribute of the HTML element with |element_id| is
184 // equal to |expected_value|. If the current value is not as expected, this
185 // waits until the "change" event is fired for the element. This also
186 // guarantees that once the real value matches the expected, the JavaScript
187 // event loop is spun to allow all other possible events to take place.
188 void WaitForElementValue(const std::string& element_id,
189 const std::string& expected_value);
190 // Checks that the current "value" attribute of the HTML element with
191 // |element_id| is equal to |expected_value|.
192 void CheckElementValue(const std::string& element_id,
193 const std::string& expected_value);
194
195 private:
196 DISALLOW_COPY_AND_ASSIGN(PasswordManagerBrowserTest);
197 };
198
WaitForElementValue(const std::string & element_id,const std::string & expected_value)199 void PasswordManagerBrowserTest::WaitForElementValue(
200 const std::string& element_id,
201 const std::string& expected_value) {
202 enum ReturnCodes { // Possible results of the JavaScript code.
203 RETURN_CODE_OK,
204 RETURN_CODE_NO_ELEMENT,
205 RETURN_CODE_WRONG_VALUE,
206 RETURN_CODE_INVALID,
207 };
208 const std::string value_check_function = base::StringPrintf(
209 "function valueCheck() {"
210 " var element = document.getElementById('%s');"
211 " return element && element.value == '%s';"
212 "}",
213 element_id.c_str(),
214 expected_value.c_str());
215 const std::string script =
216 value_check_function +
217 base::StringPrintf(
218 "if (valueCheck()) {"
219 " /* Spin the event loop with setTimeout. */"
220 " setTimeout(window.domAutomationController.send(%d), 0);"
221 "} else {"
222 " var element = document.getElementById('%s');"
223 " if (!element)"
224 " window.domAutomationController.send(%d);"
225 " element.onchange = function() {"
226 " if (valueCheck()) {"
227 " /* Spin the event loop with setTimeout. */"
228 " setTimeout(window.domAutomationController.send(%d), 0);"
229 " } else {"
230 " window.domAutomationController.send(%d);"
231 " }"
232 " };"
233 "}",
234 RETURN_CODE_OK,
235 element_id.c_str(),
236 RETURN_CODE_NO_ELEMENT,
237 RETURN_CODE_OK,
238 RETURN_CODE_WRONG_VALUE);
239 int return_value = RETURN_CODE_INVALID;
240 ASSERT_TRUE(content::ExecuteScriptAndExtractInt(
241 RenderViewHost(), script, &return_value));
242 EXPECT_EQ(RETURN_CODE_OK, return_value)
243 << "element_id = " << element_id
244 << ", expected_value = " << expected_value;
245 }
246
CheckElementValue(const std::string & element_id,const std::string & expected_value)247 void PasswordManagerBrowserTest::CheckElementValue(
248 const std::string& element_id,
249 const std::string& expected_value) {
250 const std::string value_check_script = base::StringPrintf(
251 "var element = document.getElementById('%s');"
252 "window.domAutomationController.send(element && element.value == '%s');",
253 element_id.c_str(),
254 expected_value.c_str());
255 bool return_value = false;
256 ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
257 RenderViewHost(), value_check_script, &return_value));
258 EXPECT_TRUE(return_value) << "element_id = " << element_id
259 << ", expected_value = " << expected_value;
260 }
261
262 // Actual tests ---------------------------------------------------------------
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,PromptForNormalSubmit)263 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
264 PromptForNormalSubmit) {
265 NavigateToFile("/password/password_form.html");
266
267 // Fill a form and submit through a <input type="submit"> button. Nothing
268 // special.
269 NavigationObserver observer(WebContents());
270 std::string fill_and_submit =
271 "document.getElementById('username_field').value = 'temp';"
272 "document.getElementById('password_field').value = 'random';"
273 "document.getElementById('input_submit_button').click()";
274 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
275 observer.Wait();
276 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
277 EXPECT_TRUE(controller()->PasswordPendingUserDecision());
278 } else {
279 EXPECT_TRUE(observer.infobar_shown());
280 }
281 }
282
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,PromptForSubmitWithInPageNavigation)283 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
284 PromptForSubmitWithInPageNavigation) {
285 NavigateToFile("/password/password_navigate_before_submit.html");
286
287 // Fill a form and submit through a <input type="submit"> button. Nothing
288 // special. The form does an in-page navigation before submitting.
289 NavigationObserver observer(WebContents());
290 std::string fill_and_submit =
291 "document.getElementById('username_field').value = 'temp';"
292 "document.getElementById('password_field').value = 'random';"
293 "document.getElementById('input_submit_button').click()";
294 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
295 observer.Wait();
296 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
297 EXPECT_TRUE(controller()->PasswordPendingUserDecision());
298 } else {
299 EXPECT_TRUE(observer.infobar_shown());
300 }
301 }
302
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,LoginSuccessWithUnrelatedForm)303 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
304 LoginSuccessWithUnrelatedForm) {
305 // Log in, see a form on the landing page. That form is not related to the
306 // login form (=has a different action), so we should offer saving the
307 // password.
308 NavigateToFile("/password/password_form.html");
309
310 NavigationObserver observer(WebContents());
311 std::string fill_and_submit =
312 "document.getElementById('username_unrelated').value = 'temp';"
313 "document.getElementById('password_unrelated').value = 'random';"
314 "document.getElementById('submit_unrelated').click()";
315 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
316 observer.Wait();
317 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
318 EXPECT_TRUE(controller()->PasswordPendingUserDecision());
319 } else {
320 EXPECT_TRUE(observer.infobar_shown());
321 }
322 }
323
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,LoginFailed)324 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, LoginFailed) {
325 // Log in, see a form on the landing page. That form is not related to the
326 // login form (=has a different action), so we should offer saving the
327 // password.
328 NavigateToFile("/password/password_form.html");
329
330 NavigationObserver observer(WebContents());
331 std::string fill_and_submit =
332 "document.getElementById('username_failed').value = 'temp';"
333 "document.getElementById('password_failed').value = 'random';"
334 "document.getElementById('submit_failed').click()";
335 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
336 observer.Wait();
337 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
338 EXPECT_FALSE(controller()->PasswordPendingUserDecision());
339 } else {
340 EXPECT_FALSE(observer.infobar_shown());
341 }
342 }
343
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,Redirects)344 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, Redirects) {
345 NavigateToFile("/password/password_form.html");
346
347 // Fill a form and submit through a <input type="submit"> button. The form
348 // points to a redirection page.
349 NavigationObserver observer(WebContents());
350 std::string fill_and_submit =
351 "document.getElementById('username_redirect').value = 'temp';"
352 "document.getElementById('password_redirect').value = 'random';"
353 "document.getElementById('submit_redirect').click()";
354 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
355 observer.disable_should_automatically_accept_infobar();
356 observer.Wait();
357 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
358 EXPECT_TRUE(controller()->PasswordPendingUserDecision());
359 } else {
360 EXPECT_TRUE(observer.infobar_shown());
361 }
362
363 // The redirection page now redirects via Javascript. We check that the
364 // infobar stays.
365 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(),
366 "window.location.href = 'done.html';"));
367 observer.Wait();
368 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
369 EXPECT_TRUE(controller()->PasswordPendingUserDecision());
370 } else {
371 EXPECT_FALSE(observer.infobar_removed());
372 }
373 }
374
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,PromptForSubmitUsingJavaScript)375 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
376 PromptForSubmitUsingJavaScript) {
377 NavigateToFile("/password/password_form.html");
378
379 // Fill a form and submit using <button> that calls submit() on the form.
380 // This should work regardless of the type of element, as long as submit() is
381 // called.
382 NavigationObserver observer(WebContents());
383 std::string fill_and_submit =
384 "document.getElementById('username_field').value = 'temp';"
385 "document.getElementById('password_field').value = 'random';"
386 "document.getElementById('submit_button').click()";
387 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
388 observer.Wait();
389 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
390 EXPECT_TRUE(controller()->PasswordPendingUserDecision());
391 } else {
392 EXPECT_TRUE(observer.infobar_shown());
393 }
394 }
395
396 // Flaky: crbug.com/301547, observed on win and mac. Probably happens on all
397 // platforms.
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,DISABLED_PromptForDynamicForm)398 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
399 DISABLED_PromptForDynamicForm) {
400 NavigateToFile("/password/dynamic_password_form.html");
401
402 // Fill the dynamic password form and submit.
403 NavigationObserver observer(WebContents());
404 std::string fill_and_submit =
405 "document.getElementById('create_form_button').click();"
406 "window.setTimeout(function() {"
407 " document.dynamic_form.username.value = 'tempro';"
408 " document.dynamic_form.password.value = 'random';"
409 " document.dynamic_form.submit();"
410 "}, 0)";
411 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
412 observer.Wait();
413 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
414 EXPECT_TRUE(controller()->PasswordPendingUserDecision());
415 } else {
416 EXPECT_TRUE(observer.infobar_shown());
417 }
418 }
419
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,NoPromptForNavigation)420 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, NoPromptForNavigation) {
421 NavigateToFile("/password/password_form.html");
422
423 // Don't fill the password form, just navigate away. Shouldn't prompt.
424 NavigationObserver observer(WebContents());
425 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(),
426 "window.location.href = 'done.html';"));
427 observer.Wait();
428 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
429 EXPECT_FALSE(controller()->PasswordPendingUserDecision());
430 } else {
431 EXPECT_FALSE(observer.infobar_shown());
432 }
433 }
434
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,NoPromptForSubFrameNavigation)435 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
436 NoPromptForSubFrameNavigation) {
437 NavigateToFile("/password/multi_frames.html");
438
439 // If you are filling out a password form in one frame and a different frame
440 // navigates, this should not trigger the infobar.
441 NavigationObserver observer(WebContents());
442 observer.SetPathToWaitFor("/password/done.html");
443 std::string fill =
444 "var first_frame = document.getElementById('first_frame');"
445 "var frame_doc = first_frame.contentDocument;"
446 "frame_doc.getElementById('username_field').value = 'temp';"
447 "frame_doc.getElementById('password_field').value = 'random';";
448 std::string navigate_frame =
449 "var second_iframe = document.getElementById('second_frame');"
450 "second_iframe.contentWindow.location.href = 'done.html';";
451
452 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill));
453 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), navigate_frame));
454 observer.Wait();
455 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
456 EXPECT_FALSE(controller()->PasswordPendingUserDecision());
457 } else {
458 EXPECT_FALSE(observer.infobar_shown());
459 }
460 }
461
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,PromptAfterSubmitWithSubFrameNavigation)462 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
463 PromptAfterSubmitWithSubFrameNavigation) {
464 NavigateToFile("/password/multi_frames.html");
465
466 // Make sure that we prompt to save password even if a sub-frame navigation
467 // happens first.
468 NavigationObserver observer(WebContents());
469 observer.SetPathToWaitFor("/password/done.html");
470 std::string navigate_frame =
471 "var second_iframe = document.getElementById('second_frame');"
472 "second_iframe.contentWindow.location.href = 'other.html';";
473 std::string fill_and_submit =
474 "var first_frame = document.getElementById('first_frame');"
475 "var frame_doc = first_frame.contentDocument;"
476 "frame_doc.getElementById('username_field').value = 'temp';"
477 "frame_doc.getElementById('password_field').value = 'random';"
478 "frame_doc.getElementById('input_submit_button').click();";
479
480 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), navigate_frame));
481 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
482 observer.Wait();
483 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
484 EXPECT_TRUE(controller()->PasswordPendingUserDecision());
485 } else {
486 EXPECT_TRUE(observer.infobar_shown());
487 }
488 }
489
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,PromptForXHRSubmit)490 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
491 PromptForXHRSubmit) {
492 #if defined(OS_WIN) && defined(USE_ASH)
493 // Disable this test in Metro+Ash for now (http://crbug.com/262796).
494 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
495 return;
496 #endif
497 NavigateToFile("/password/password_xhr_submit.html");
498
499 // Verify that we show the save password prompt if a form returns false
500 // in its onsubmit handler but instead logs in/navigates via XHR.
501 // Note that calling 'submit()' on a form with javascript doesn't call
502 // the onsubmit handler, so we click the submit button instead.
503 NavigationObserver observer(WebContents());
504 std::string fill_and_submit =
505 "document.getElementById('username_field').value = 'temp';"
506 "document.getElementById('password_field').value = 'random';"
507 "document.getElementById('submit_button').click()";
508 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
509 observer.Wait();
510 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
511 EXPECT_TRUE(controller()->PasswordPendingUserDecision());
512 } else {
513 EXPECT_TRUE(observer.infobar_shown());
514 }
515 }
516
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,PromptForXHRWithoutOnSubmit)517 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
518 PromptForXHRWithoutOnSubmit) {
519 NavigateToFile("/password/password_xhr_submit.html");
520
521 // Verify that if XHR navigation occurs and the form is properly filled out,
522 // we try and save the password even though onsubmit hasn't been called.
523 NavigationObserver observer(WebContents());
524 std::string fill_and_navigate =
525 "document.getElementById('username_field').value = 'temp';"
526 "document.getElementById('password_field').value = 'random';"
527 "send_xhr()";
528 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_navigate));
529 observer.Wait();
530 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
531 EXPECT_TRUE(controller()->PasswordPendingUserDecision());
532 } else {
533 EXPECT_TRUE(observer.infobar_shown());
534 }
535 }
536
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,NoPromptIfLinkClicked)537 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
538 NoPromptIfLinkClicked) {
539 NavigateToFile("/password/password_form.html");
540
541 // Verify that if the user takes a direct action to leave the page, we don't
542 // prompt to save the password even if the form is already filled out.
543 NavigationObserver observer(WebContents());
544 std::string fill_and_click_link =
545 "document.getElementById('username_field').value = 'temp';"
546 "document.getElementById('password_field').value = 'random';"
547 "document.getElementById('link').click();";
548 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_click_link));
549 observer.Wait();
550 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
551 EXPECT_FALSE(controller()->PasswordPendingUserDecision());
552 } else {
553 EXPECT_FALSE(observer.infobar_shown());
554 }
555 }
556
557 // TODO(jam): http://crbug.com/350550
558 #if !defined(OS_WIN)
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,VerifyPasswordGenerationUpload)559 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
560 VerifyPasswordGenerationUpload) {
561 // Prevent Autofill requests from actually going over the wire.
562 net::TestURLFetcherFactory factory;
563 // Disable Autofill requesting access to AddressBook data. This causes
564 // the test to hang on Mac.
565 autofill::test::DisableSystemServices(browser()->profile()->GetPrefs());
566
567 // Visit a signup form.
568 NavigateToFile("/password/signup_form.html");
569
570 // Enter a password and save it.
571 NavigationObserver first_observer(WebContents());
572 std::string fill_and_submit =
573 "document.getElementById('other_info').value = 'stuff';"
574 "document.getElementById('username_field').value = 'my_username';"
575 "document.getElementById('password_field').value = 'password';"
576 "document.getElementById('input_submit_button').click()";
577 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
578
579 first_observer.Wait();
580 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
581 ASSERT_TRUE(controller()->PasswordPendingUserDecision());
582 controller()->SavePassword();
583 } else {
584 ASSERT_TRUE(first_observer.infobar_shown());
585 }
586
587 // Now navigate to a login form that has similar HTML markup.
588 NavigateToFile("/password/password_form.html");
589
590 // Simulate a user click to force an autofill of the form's DOM value, not
591 // just the suggested value.
592 content::SimulateMouseClick(
593 WebContents(), 0, blink::WebMouseEvent::ButtonLeft);
594
595 // The form should be filled with the previously submitted username.
596 std::string get_username =
597 "window.domAutomationController.send("
598 "document.getElementById('username_field').value);";
599 std::string actual_username;
600 ASSERT_TRUE(content::ExecuteScriptAndExtractString(RenderViewHost(),
601 get_username,
602 &actual_username));
603 ASSERT_EQ("my_username", actual_username);
604
605 // Submit the form and verify that there is no infobar (as the password
606 // has already been saved).
607 NavigationObserver second_observer(WebContents());
608 std::string submit_form =
609 "document.getElementById('input_submit_button').click()";
610 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit_form));
611 second_observer.Wait();
612 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
613 EXPECT_FALSE(controller()->PasswordPendingUserDecision());
614 } else {
615 EXPECT_FALSE(second_observer.infobar_shown());
616 }
617
618 // Verify that we sent a ping to Autofill saying that the original form
619 // was likely an account creation form since it has more than 2 text input
620 // fields and was used for the first time on a different form.
621 base::HistogramBase* upload_histogram =
622 base::StatisticsRecorder::FindHistogram(
623 "PasswordGeneration.UploadStarted");
624 ASSERT_TRUE(upload_histogram);
625 scoped_ptr<base::HistogramSamples> snapshot =
626 upload_histogram->SnapshotSamples();
627 EXPECT_EQ(0, snapshot->GetCount(0 /* failure */));
628 EXPECT_EQ(1, snapshot->GetCount(1 /* success */));
629 }
630 #endif
631
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,PromptForSubmitFromIframe)632 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, PromptForSubmitFromIframe) {
633 NavigateToFile("/password/password_submit_from_iframe.html");
634
635 // Submit a form in an iframe, then cause the whole page to navigate without a
636 // user gesture. We expect the save password prompt to be shown here, because
637 // some pages use such iframes for login forms.
638 NavigationObserver observer(WebContents());
639 std::string fill_and_submit =
640 "var iframe = document.getElementById('test_iframe');"
641 "var iframe_doc = iframe.contentDocument;"
642 "iframe_doc.getElementById('username_field').value = 'temp';"
643 "iframe_doc.getElementById('password_field').value = 'random';"
644 "iframe_doc.getElementById('submit_button').click()";
645
646 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
647 observer.Wait();
648 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
649 EXPECT_TRUE(controller()->PasswordPendingUserDecision());
650 } else {
651 EXPECT_TRUE(observer.infobar_shown());
652 }
653 }
654
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,PromptForInputElementWithoutName)655 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
656 PromptForInputElementWithoutName) {
657 // Check that the prompt is shown for forms where input elements lack the
658 // "name" attribute but the "id" is present.
659 NavigateToFile("/password/password_form.html");
660
661 NavigationObserver observer(WebContents());
662 std::string fill_and_submit =
663 "document.getElementById('username_field_no_name').value = 'temp';"
664 "document.getElementById('password_field_no_name').value = 'random';"
665 "document.getElementById('input_submit_button_no_name').click()";
666 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
667 observer.Wait();
668 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
669 EXPECT_TRUE(controller()->PasswordPendingUserDecision());
670 } else {
671 EXPECT_TRUE(observer.infobar_shown());
672 }
673 }
674
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,PromptForInputElementWithoutId)675 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
676 PromptForInputElementWithoutId) {
677 // Check that the prompt is shown for forms where input elements lack the
678 // "id" attribute but the "name" attribute is present.
679 NavigateToFile("/password/password_form.html");
680
681 NavigationObserver observer(WebContents());
682 std::string fill_and_submit =
683 "document.getElementsByName('username_field_no_id')[0].value = 'temp';"
684 "document.getElementsByName('password_field_no_id')[0].value = 'random';"
685 "document.getElementsByName('input_submit_button_no_id')[0].click()";
686 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
687 observer.Wait();
688 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
689 EXPECT_TRUE(controller()->PasswordPendingUserDecision());
690 } else {
691 EXPECT_TRUE(observer.infobar_shown());
692 }
693 }
694
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,NoPromptForInputElementWithoutIdAndName)695 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
696 NoPromptForInputElementWithoutIdAndName) {
697 // Check that no prompt is shown for forms where the input fields lack both
698 // the "id" and the "name" attributes.
699 NavigateToFile("/password/password_form.html");
700
701 NavigationObserver observer(WebContents());
702 std::string fill_and_submit =
703 "var form = document.getElementById('testform_elements_no_id_no_name');"
704 "var username = form.children[0];"
705 "username.value = 'temp';"
706 "var password = form.children[1];"
707 "password.value = 'random';"
708 "form.children[2].click()"; // form.children[2] is the submit button.
709 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
710 observer.Wait();
711 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
712 EXPECT_FALSE(controller()->PasswordPendingUserDecision());
713 } else {
714 EXPECT_FALSE(observer.infobar_shown());
715 }
716 }
717
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,DeleteFrameBeforeSubmit)718 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, DeleteFrameBeforeSubmit) {
719 NavigateToFile("/password/multi_frames.html");
720
721 NavigationObserver observer(WebContents());
722 // Make sure we save some password info from an iframe and then destroy it.
723 std::string save_and_remove =
724 "var first_frame = document.getElementById('first_frame');"
725 "var frame_doc = first_frame.contentDocument;"
726 "frame_doc.getElementById('username_field').value = 'temp';"
727 "frame_doc.getElementById('password_field').value = 'random';"
728 "frame_doc.getElementById('input_submit_button').click();"
729 "first_frame.parentNode.removeChild(first_frame);";
730 // Submit from the main frame, but without navigating through the onsubmit
731 // handler.
732 std::string navigate_frame =
733 "document.getElementById('username_field').value = 'temp';"
734 "document.getElementById('password_field').value = 'random';"
735 "document.getElementById('input_submit_button').click();"
736 "window.location.href = 'done.html';";
737
738 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), save_and_remove));
739 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), navigate_frame));
740 observer.Wait();
741 // The only thing we check here is that there is no use-after-free reported.
742 }
743
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,PasswordValueAccessible)744 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, PasswordValueAccessible) {
745 NavigateToFile("/password/form_and_link.html");
746
747 // Click on a link to open a new tab, then switch back to the first one.
748 EXPECT_EQ(1, browser()->tab_strip_model()->count());
749 std::string click =
750 "document.getElementById('testlink').click();";
751 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), click));
752 EXPECT_EQ(2, browser()->tab_strip_model()->count());
753 browser()->tab_strip_model()->ActivateTabAt(0, false);
754
755 // Fill in the credentials, and make sure they are saved.
756 NavigationObserver form_submit_observer(WebContents());
757 std::string fill_and_submit =
758 "document.getElementById('username_field').value = 'temp';"
759 "document.getElementById('password_field').value = 'random';"
760 "document.getElementById('input_submit_button').click();";
761 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
762 form_submit_observer.Wait();
763 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
764 EXPECT_TRUE(controller()->PasswordPendingUserDecision());
765 controller()->SavePassword();
766 } else {
767 EXPECT_TRUE(form_submit_observer.infobar_shown());
768 }
769
770 // Reload the original page to have the saved credentials autofilled.
771 NavigationObserver reload_observer(WebContents());
772 NavigateToFile("/password/form_and_link.html");
773 reload_observer.Wait();
774
775 // Wait until the username is filled, to make sure autofill kicked in.
776 WaitForElementValue("username_field", "temp");
777 // Now check that the password is not accessible yet.
778 CheckElementValue("password_field", "");
779 // Let the user interact with the page.
780 content::SimulateMouseClickAt(
781 WebContents(), 0, blink::WebMouseEvent::ButtonLeft, gfx::Point(1, 1));
782 // Wait until that interaction causes the password value to be revealed.
783 WaitForElementValue("password_field", "random");
784 // And check that after the side-effects of the interaction took place, the
785 // username value stays the same.
786 CheckElementValue("username_field", "temp");
787 }
788
789 // The following test is limited to Aura, because
790 // RenderWidgetHostViewGuest::ProcessAckedTouchEvent is, and
791 // ProcessAckedTouchEvent is what triggers the translation of touch events to
792 // gesture events.
793 #if defined(USE_AURA)
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,PasswordValueAccessibleOnSubmit)794 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
795 PasswordValueAccessibleOnSubmit) {
796 NavigateToFile("/password/form_and_link.html");
797
798 // Fill in the credentials, and make sure they are saved.
799 NavigationObserver form_submit_observer(WebContents());
800 std::string fill_and_submit =
801 "document.getElementById('username_field').value = 'temp';"
802 "document.getElementById('password_field').value = 'random_secret';"
803 "document.getElementById('input_submit_button').click();";
804 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
805 form_submit_observer.Wait();
806 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
807 EXPECT_TRUE(controller()->PasswordPendingUserDecision());
808 controller()->SavePassword();
809 } else {
810 EXPECT_TRUE(form_submit_observer.infobar_shown());
811 }
812
813 // Reload the original page to have the saved credentials autofilled.
814 NavigationObserver reload_observer(WebContents());
815 NavigateToFile("/password/form_and_link.html");
816 reload_observer.Wait();
817
818 NavigationObserver submit_observer(WebContents());
819 // Submit the form via a tap on the submit button. The button is placed at 0,
820 // 100, and has height 300 and width 700.
821 content::SimulateTapAt(WebContents(), gfx::Point(350, 250));
822 submit_observer.Wait();
823 std::string query = WebContents()->GetURL().query();
824 EXPECT_NE(std::string::npos, query.find("random_secret")) << query;
825 }
826 #endif
827
828 // Test fix for crbug.com/338650.
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,DontPromptForPasswordFormWithDefaultValue)829 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
830 DontPromptForPasswordFormWithDefaultValue) {
831 NavigateToFile("/password/password_form_with_default_value.html");
832
833 // Don't prompt if we navigate away even if there is a password value since
834 // it's not coming from the user.
835 NavigationObserver observer(WebContents());
836 NavigateToFile("/password/done.html");
837 observer.Wait();
838 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
839 EXPECT_FALSE(controller()->PasswordPendingUserDecision());
840 } else {
841 EXPECT_FALSE(observer.infobar_shown());
842 }
843 }
844
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,PromptWhenEnableAutomaticPasswordSavingSwitchIsNotSet)845 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
846 PromptWhenEnableAutomaticPasswordSavingSwitchIsNotSet) {
847 NavigateToFile("/password/password_form.html");
848
849 // Fill a form and submit through a <input type="submit"> button.
850 NavigationObserver observer(WebContents());
851 std::string fill_and_submit =
852 "document.getElementById('username_field').value = 'temp';"
853 "document.getElementById('password_field').value = 'random';"
854 "document.getElementById('input_submit_button').click()";
855 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
856 observer.Wait();
857 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
858 EXPECT_TRUE(controller()->PasswordPendingUserDecision());
859 } else {
860 EXPECT_TRUE(observer.infobar_shown());
861 }
862 }
863
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,DontPromptWhenEnableAutomaticPasswordSavingSwitchIsSet)864 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
865 DontPromptWhenEnableAutomaticPasswordSavingSwitchIsSet) {
866 password_manager::TestPasswordStore* password_store =
867 static_cast<password_manager::TestPasswordStore*>(
868 PasswordStoreFactory::GetForProfile(browser()->profile(),
869 Profile::IMPLICIT_ACCESS).get());
870
871 EXPECT_TRUE(password_store->IsEmpty());
872
873 NavigateToFile("/password/password_form.html");
874
875 // Add the enable-automatic-password-saving switch.
876 CommandLine::ForCurrentProcess()->AppendSwitch(
877 password_manager::switches::kEnableAutomaticPasswordSaving);
878
879 // Fill a form and submit through a <input type="submit"> button.
880 NavigationObserver observer(WebContents());
881 // Make sure that the only passwords saved are the auto-saved ones.
882 observer.disable_should_automatically_accept_infobar();
883 std::string fill_and_submit =
884 "document.getElementById('username_field').value = 'temp';"
885 "document.getElementById('password_field').value = 'random';"
886 "document.getElementById('input_submit_button').click()";
887 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
888 observer.Wait();
889 if (chrome::VersionInfo::GetChannel() ==
890 chrome::VersionInfo::CHANNEL_UNKNOWN) {
891 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
892 EXPECT_FALSE(controller()->PasswordPendingUserDecision());
893 } else {
894 EXPECT_FALSE(observer.infobar_shown());
895 }
896 EXPECT_FALSE(password_store->IsEmpty());
897 } else {
898 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
899 EXPECT_TRUE(controller()->PasswordPendingUserDecision());
900 } else {
901 EXPECT_TRUE(observer.infobar_shown());
902 }
903 EXPECT_TRUE(password_store->IsEmpty());
904 }
905 }
906
907 // Test fix for crbug.com/368690.
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,NoPromptWhenReloading)908 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, NoPromptWhenReloading) {
909 NavigateToFile("/password/password_form.html");
910
911 std::string fill =
912 "document.getElementById('username_redirect').value = 'temp';"
913 "document.getElementById('password_redirect').value = 'random';";
914 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill));
915
916 NavigationObserver observer(WebContents());
917 GURL url = embedded_test_server()->GetURL("/password/password_form.html");
918 chrome::NavigateParams params(browser(), url,
919 content::PAGE_TRANSITION_RELOAD);
920 ui_test_utils::NavigateToURL(¶ms);
921 observer.Wait();
922 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
923 EXPECT_FALSE(controller()->PasswordPendingUserDecision());
924 } else {
925 EXPECT_FALSE(observer.infobar_shown());
926 }
927 }
928
929 // Test that if a form gets dynamically added between the form parsing and
930 // rendering, and while the main frame still loads, it still is registered, and
931 // thus saving passwords from it works.
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,FormsAddedBetweenParsingAndRendering)932 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
933 FormsAddedBetweenParsingAndRendering) {
934 NavigateToFile("/password/between_parsing_and_rendering.html");
935
936 NavigationObserver observer(WebContents());
937 std::string submit =
938 "document.getElementById('username').value = 'temp';"
939 "document.getElementById('password').value = 'random';"
940 "document.getElementById('submit-button').click();";
941 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit));
942 observer.Wait();
943
944 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
945 EXPECT_TRUE(controller()->PasswordPendingUserDecision());
946 } else {
947 EXPECT_TRUE(observer.infobar_shown());
948 }
949 }
950