Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(230)

Side by Side Diff: chrome/browser/password_manager/password_manager_browsertest.cc

Issue 1159513002: Allow autofill in iframe inside page of same origin (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <string> 5 #include <string>
6 6
7 #include "base/command_line.h" 7 #include "base/command_line.h"
8 #include "base/metrics/histogram_samples.h" 8 #include "base/metrics/histogram_samples.h"
9 #include "base/metrics/statistics_recorder.h" 9 #include "base/metrics/statistics_recorder.h"
10 #include "base/path_service.h" 10 #include "base/path_service.h"
(...skipping 30 matching lines...) Expand all
41 #include "content/public/browser/navigation_controller.h" 41 #include "content/public/browser/navigation_controller.h"
42 #include "content/public/browser/notification_service.h" 42 #include "content/public/browser/notification_service.h"
43 #include "content/public/browser/render_frame_host.h" 43 #include "content/public/browser/render_frame_host.h"
44 #include "content/public/browser/render_view_host.h" 44 #include "content/public/browser/render_view_host.h"
45 #include "content/public/browser/web_contents.h" 45 #include "content/public/browser/web_contents.h"
46 #include "content/public/browser/web_contents_observer.h" 46 #include "content/public/browser/web_contents_observer.h"
47 #include "content/public/common/content_switches.h" 47 #include "content/public/common/content_switches.h"
48 #include "content/public/test/browser_test_utils.h" 48 #include "content/public/test/browser_test_utils.h"
49 #include "content/public/test/test_utils.h" 49 #include "content/public/test/test_utils.h"
50 #include "net/base/filename_util.h" 50 #include "net/base/filename_util.h"
51 #include "net/dns/mock_host_resolver.h"
51 #include "net/test/embedded_test_server/embedded_test_server.h" 52 #include "net/test/embedded_test_server/embedded_test_server.h"
52 #include "net/test/embedded_test_server/http_request.h" 53 #include "net/test/embedded_test_server/http_request.h"
53 #include "net/test/embedded_test_server/http_response.h" 54 #include "net/test/embedded_test_server/http_response.h"
54 #include "net/test/spawned_test_server/spawned_test_server.h" 55 #include "net/test/spawned_test_server/spawned_test_server.h"
55 #include "net/url_request/test_url_fetcher_factory.h" 56 #include "net/url_request/test_url_fetcher_factory.h"
56 #include "testing/gmock/include/gmock/gmock.h" 57 #include "testing/gmock/include/gmock/gmock.h"
57 #include "third_party/WebKit/public/web/WebInputEvent.h" 58 #include "third_party/WebKit/public/web/WebInputEvent.h"
58 #include "ui/events/keycodes/keyboard_codes.h" 59 #include "ui/events/keycodes/keyboard_codes.h"
59 #include "ui/gfx/geometry/point.h" 60 #include "ui/gfx/geometry/point.h"
60 61
(...skipping 221 matching lines...) Expand 10 before | Expand all | Expand 10 after
282 } else { 283 } else {
283 scoped_ptr<net::test_server::BasicHttpResponse> http_response( 284 scoped_ptr<net::test_server::BasicHttpResponse> http_response(
284 new net::test_server::BasicHttpResponse); 285 new net::test_server::BasicHttpResponse);
285 http_response->set_code(net::HTTP_UNAUTHORIZED); 286 http_response->set_code(net::HTTP_UNAUTHORIZED);
286 http_response->AddCustomHeader("WWW-Authenticate", 287 http_response->AddCustomHeader("WWW-Authenticate",
287 "Basic realm=\"test realm\""); 288 "Basic realm=\"test realm\"");
288 return http_response.Pass(); 289 return http_response.Pass();
289 } 290 }
290 } 291 }
291 292
293 // Handle |request| to arbitrary domain and respond with |response_content_|
294 std::string response_content_;
295 scoped_ptr<net::test_server::HttpResponse> HandleTestRequest(
Garrett Casto 2015/05/26 20:31:37 So after thinking about it some more, I don't thin
296 const net::test_server::HttpRequest& request) {
297 scoped_ptr<net::test_server::BasicHttpResponse> http_response(
298 new net::test_server::BasicHttpResponse());
299 http_response->set_code(net::HTTP_OK);
300 http_response->set_content_type("text/html");
301 if (request.relative_url == "/done.html") {
Garrett Casto 2015/05/26 20:31:37 Looks like this could be solved by having the path
302 std::string alt_response =
303 "<html>"
304 "<body>"
305 "<a id='link' href='done.html'>Go somewhere</a>"
306 "</body>"
307 "</html>";
308 http_response->set_content(alt_response);
309 } else {
310 http_response->set_content(response_content_);
311 }
312 return http_response.Pass();
313 }
314
315
292 } // namespace 316 } // namespace
293 317
294 318
295 // PasswordManagerBrowserTest ------------------------------------------------- 319 // PasswordManagerBrowserTest -------------------------------------------------
296 320
297 class PasswordManagerBrowserTest : public InProcessBrowserTest { 321 class PasswordManagerBrowserTest : public InProcessBrowserTest {
298 public: 322 public:
299 PasswordManagerBrowserTest() {} 323 PasswordManagerBrowserTest() {}
300 ~PasswordManagerBrowserTest() override {} 324 ~PasswordManagerBrowserTest() override {}
301 325
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
338 observer.Wait(); 362 observer.Wait();
339 } 363 }
340 364
341 // Waits until the "value" attribute of the HTML element with |element_id| is 365 // Waits until the "value" attribute of the HTML element with |element_id| is
342 // equal to |expected_value|. If the current value is not as expected, this 366 // equal to |expected_value|. If the current value is not as expected, this
343 // waits until the "change" event is fired for the element. This also 367 // waits until the "change" event is fired for the element. This also
344 // guarantees that once the real value matches the expected, the JavaScript 368 // guarantees that once the real value matches the expected, the JavaScript
345 // event loop is spun to allow all other possible events to take place. 369 // event loop is spun to allow all other possible events to take place.
346 void WaitForElementValue(const std::string& element_id, 370 void WaitForElementValue(const std::string& element_id,
347 const std::string& expected_value); 371 const std::string& expected_value);
372 // Same as above except the element |element_id| is in iframe |iframe_id|
373 void WaitForElementValue(const std::string& iframe_id,
374 const std::string& element_id,
375 const std::string& expected_value);
348 // Checks that the current "value" attribute of the HTML element with 376 // Checks that the current "value" attribute of the HTML element with
349 // |element_id| is equal to |expected_value|. 377 // |element_id| is equal to |expected_value|.
350 void CheckElementValue(const std::string& element_id, 378 void CheckElementValue(const std::string& element_id,
351 const std::string& expected_value); 379 const std::string& expected_value);
380 // Same as above except the element |element_id| is in iframe |iframe_id|
381 void CheckElementValue(const std::string& iframe_id,
382 const std::string& element_id,
383 const std::string& expected_value);
352 384
353 // TODO(dvadym): Remove this once history.pushState() handling is not behind 385 // TODO(dvadym): Remove this once history.pushState() handling is not behind
354 // a flag. 386 // a flag.
355 // Calling this will activate handling of pushState()-initiated form submits. 387 // Calling this will activate handling of pushState()-initiated form submits.
356 // This feature is currently behind a renderer flag. Just setting the flag in 388 // This feature is currently behind a renderer flag. Just setting the flag in
357 // the browser will not change it for the already running renderer at tab 0. 389 // the browser will not change it for the already running renderer at tab 0.
358 // Therefore this method first sets the flag and then opens a new tab at 390 // Therefore this method first sets the flag and then opens a new tab at
359 // position 0. This new tab gets the flag copied from the browser process. 391 // position 0. This new tab gets the flag copied from the browser process.
360 void ActivateHistoryPushState(); 392 void ActivateHistoryPushState();
361 393
362 private: 394 private:
363 DISALLOW_COPY_AND_ASSIGN(PasswordManagerBrowserTest); 395 DISALLOW_COPY_AND_ASSIGN(PasswordManagerBrowserTest);
364 }; 396 };
365 397
366 void PasswordManagerBrowserTest::WaitForElementValue( 398 void PasswordManagerBrowserTest::WaitForElementValue(
367 const std::string& element_id, 399 const std::string& element_id,
368 const std::string& expected_value) { 400 const std::string& expected_value) {
401 PasswordManagerBrowserTest::WaitForElementValue("null",
402 element_id,
403 expected_value);
404 }
405
406 void PasswordManagerBrowserTest::WaitForElementValue(
407 const std::string& iframe_id,
408 const std::string& element_id,
409 const std::string& expected_value) {
369 enum ReturnCodes { // Possible results of the JavaScript code. 410 enum ReturnCodes { // Possible results of the JavaScript code.
370 RETURN_CODE_OK, 411 RETURN_CODE_OK,
371 RETURN_CODE_NO_ELEMENT, 412 RETURN_CODE_NO_ELEMENT,
372 RETURN_CODE_WRONG_VALUE, 413 RETURN_CODE_WRONG_VALUE,
373 RETURN_CODE_INVALID, 414 RETURN_CODE_INVALID,
374 }; 415 };
375 const std::string value_check_function = base::StringPrintf( 416 const std::string value_check_function = base::StringPrintf(
376 "function valueCheck() {" 417 "function valueCheck() {"
377 " var element = document.getElementById('%s');" 418 " var element;"
Garrett Casto 2015/05/26 20:31:37 This is a counter intuitive, but this line isn't n
419 " if (%s)"
420 " var element = document.getElementById("
421 " '%s').contentDocument.getElementById('%s');"
422 " else "
423 " var element = document.getElementById('%s');"
378 " return element && element.value == '%s';" 424 " return element && element.value == '%s';"
379 "}", 425 "}",
426 iframe_id.c_str(),
427 iframe_id.c_str(),
428 element_id.c_str(),
380 element_id.c_str(), 429 element_id.c_str(),
381 expected_value.c_str()); 430 expected_value.c_str());
382 const std::string script = 431 const std::string script =
383 value_check_function + 432 value_check_function +
384 base::StringPrintf( 433 base::StringPrintf(
385 "if (valueCheck()) {" 434 "if (valueCheck()) {"
386 " /* Spin the event loop with setTimeout. */" 435 " /* Spin the event loop with setTimeout. */"
387 " setTimeout(window.domAutomationController.send(%d), 0);" 436 " setTimeout(window.domAutomationController.send(%d), 0);"
388 "} else {" 437 "} else {"
389 " var element = document.getElementById('%s');" 438 " var element;"
Garrett Casto 2015/05/26 20:31:37 Same here.
439 " if (%s)"
440 " var element = document.getElementById("
441 " '%s').contentDocument.getElementById('%s');"
442 " else "
443 " var element = document.getElementById('%s');"
390 " if (!element)" 444 " if (!element)"
391 " window.domAutomationController.send(%d);" 445 " window.domAutomationController.send(%d);"
392 " element.onchange = function() {" 446 " element.onchange = function() {"
393 " if (valueCheck()) {" 447 " if (valueCheck()) {"
394 " /* Spin the event loop with setTimeout. */" 448 " /* Spin the event loop with setTimeout. */"
395 " setTimeout(window.domAutomationController.send(%d), 0);" 449 " setTimeout(window.domAutomationController.send(%d), 0);"
396 " } else {" 450 " } else {"
397 " window.domAutomationController.send(%d);" 451 " window.domAutomationController.send(%d);"
398 " }" 452 " }"
399 " };" 453 " };"
400 "}", 454 "}",
401 RETURN_CODE_OK, 455 RETURN_CODE_OK,
456 iframe_id.c_str(),
457 iframe_id.c_str(),
458 element_id.c_str(),
402 element_id.c_str(), 459 element_id.c_str(),
403 RETURN_CODE_NO_ELEMENT, 460 RETURN_CODE_NO_ELEMENT,
404 RETURN_CODE_OK, 461 RETURN_CODE_OK,
405 RETURN_CODE_WRONG_VALUE); 462 RETURN_CODE_WRONG_VALUE);
406 int return_value = RETURN_CODE_INVALID; 463 int return_value = RETURN_CODE_INVALID;
407 ASSERT_TRUE(content::ExecuteScriptAndExtractInt( 464 ASSERT_TRUE(content::ExecuteScriptAndExtractInt(
408 RenderViewHost(), script, &return_value)); 465 RenderViewHost(), script, &return_value));
409 EXPECT_EQ(RETURN_CODE_OK, return_value) 466 EXPECT_EQ(RETURN_CODE_OK, return_value)
410 << "element_id = " << element_id 467 << "element_id = " << element_id
411 << ", expected_value = " << expected_value; 468 << ", expected_value = " << expected_value;
412 } 469 }
413 470
414 void PasswordManagerBrowserTest::CheckElementValue( 471 void PasswordManagerBrowserTest::CheckElementValue(
415 const std::string& element_id, 472 const std::string& element_id,
416 const std::string& expected_value) { 473 const std::string& expected_value) {
474 PasswordManagerBrowserTest::CheckElementValue("null",
475 element_id,
476 expected_value);
477 }
478
479 void PasswordManagerBrowserTest::CheckElementValue(
480 const std::string& iframe_id,
481 const std::string& element_id,
482 const std::string& expected_value) {
417 const std::string value_check_script = base::StringPrintf( 483 const std::string value_check_script = base::StringPrintf(
418 "var element = document.getElementById('%s');" 484 "var element;"
Garrett Casto 2015/05/26 20:31:37 Same here.
485 "if (%s)"
486 " var element = document.getElementById("
487 " '%s').contentDocument.getElementById('%s');"
488 "else "
489 " var element = document.getElementById('%s');"
419 "window.domAutomationController.send(element && element.value == '%s');", 490 "window.domAutomationController.send(element && element.value == '%s');",
491 iframe_id.c_str(),
492 iframe_id.c_str(),
493 element_id.c_str(),
420 element_id.c_str(), 494 element_id.c_str(),
421 expected_value.c_str()); 495 expected_value.c_str());
422 bool return_value = false; 496 bool return_value = false;
423 ASSERT_TRUE(content::ExecuteScriptAndExtractBool( 497 ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
424 RenderViewHost(), value_check_script, &return_value)); 498 RenderViewHost(), value_check_script, &return_value));
425 EXPECT_TRUE(return_value) << "element_id = " << element_id 499 EXPECT_TRUE(return_value) << "element_id = " << element_id
426 << ", expected_value = " << expected_value; 500 << ", expected_value = " << expected_value;
427 } 501 }
428 502
429 void PasswordManagerBrowserTest::ActivateHistoryPushState() { 503 void PasswordManagerBrowserTest::ActivateHistoryPushState() {
(...skipping 1476 matching lines...) Expand 10 before | Expand all | Expand 10 after
1906 NavigateToFile("/password/password_xhr_submit.html"); 1980 NavigateToFile("/password/password_xhr_submit.html");
1907 1981
1908 // Let the user interact with the page, so that DOM gets modification events, 1982 // Let the user interact with the page, so that DOM gets modification events,
1909 // needed for autofilling fields. 1983 // needed for autofilling fields.
1910 content::SimulateMouseClickAt( 1984 content::SimulateMouseClickAt(
1911 WebContents(), 0, blink::WebMouseEvent::ButtonLeft, gfx::Point(1, 1)); 1985 WebContents(), 0, blink::WebMouseEvent::ButtonLeft, gfx::Point(1, 1));
1912 1986
1913 // Wait until that interaction causes the password value to be revealed. 1987 // Wait until that interaction causes the password value to be revealed.
1914 WaitForElementValue("password_field", "mypassword"); 1988 WaitForElementValue("password_field", "mypassword");
1915 } 1989 }
1990
1991 // Check that a password form in an iframe of different origin will not be
1992 // filled in until a user interact with the form.
1993 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, CrossSiteIframeNotFillTest) {
1994 // Setup the mock host resolver
1995 host_resolver()->AddRule("*", "127.0.0.1");
1996 response_content_ = "<html>"
1997 "<head>"
1998 " <meta charset='utf-8' />"
1999 " <title>Title</title>"
2000 "</head>"
2001 "<body id='inframe'>"
2002 "<script>"
2003 "function receiveMessage(event){"
2004 " if (event.data == 'init_fill') { "
Garrett Casto 2015/05/26 20:31:37 Maybe this message should be "fill_and_submit" sin
2005 " document.getElementById("
2006 " 'username').value = 'temp';"
2007 " document.getElementById("
2008 " 'password').value = 'pa55w0rd';"
2009 " document.getElementById('submit_button').click();"
2010 " } else if (event.data == 'get_username') {"
2011 " event.source.postMessage("
2012 " document.getElementById('username').value,"
2013 " event.origin);"
2014 " } else if (event.data == 'get_password') {"
2015 " event.source.postMessage("
2016 " document.getElementById('password').value,"
2017 " event.origin);"
2018 " } else if (event.data == 'check_for_username') {"
Garrett Casto 2015/05/26 20:31:37 These two "check" messages don't look like they ar
2019 " waitForValue('username', 'temp', event)"
2020 " } else if (event.data == 'check_for_password') {"
2021 " waitForValue('password', 'pa55w0rd', event)"
2022 " }"
2023 "}"
2024 "window.addEventListener('message',"
2025 " receiveMessage, false);"
2026 ""
2027 "function valueCheck(id, expect_value) {"
Garrett Casto 2015/05/26 20:31:37 Looks like these two functions aren't necessary.
2028 " var element = document.getElementById(id);"
2029 " return element && element.value == expect_value;"
2030 "}"
2031 ""
2032 "function waitForValue(id, expect_value, event){"
2033 " var element = document.getElementById(id);"
2034 " if (valueCheck(id, expect_value)) {"
2035 " /* Spin the event loop with setTimeout. */"
2036 " setTimeout(event.source.postMessage("
2037 " element.value, event.origin), 0);"
2038 " } else {"
2039 " element.onchange = function() {"
2040 " if (valueCheck(id, expect_value)) {"
2041 " /* Spin the event loop with setTimeout. */"
2042 " setTimeout(event.source.postMessage("
2043 " element.value, event.origin), 0);"
2044 " }"
2045 " };"
2046 " }"
2047 "}"
2048 ""
2049 "</script>"
2050 "<form method='POST' name='TestForm' action='done.html'>"
2051 " <input type='text' id='username'/>"
2052 " <input type='password' id='password'/>"
2053 " <input type='submit' id='submit_button' value='Log'/>"
2054 "</form>"
2055 "</body>"
2056 "</html>";
2057 // Set up customized request handler
2058 embedded_test_server()->RegisterRequestHandler(
2059 base::Bind(&HandleTestRequest));
2060
2061 // Visit the sign-up form to store a password for autofill later
Garrett Casto 2015/05/26 20:31:38 Nit: The code right after this comment actually se
2062 NavigateToFile("/password/password_form_in_crosssite_iframe.html");
2063 NavigationObserver ifrm_observer(WebContents());
2064 ifrm_observer.SetPathToWaitFor("/randomFile.html");
2065
2066 std::string submit = base::StringPrintf(
Garrett Casto 2015/05/26 20:31:37 Nit: I'd name this string something more like "cre
2067 "createCrossSiteIframe('http://randomsite.net:%d/randomFile.html');",
2068 embedded_test_server()->port());
2069
2070 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit));
2071 ifrm_observer.Wait();
2072 ifrm_observer.SetPathToWaitFor("");
Garrett Casto 2015/05/26 20:31:37 This line looks superfluous as you change the stri
2073
2074 NavigationObserver init_observer(WebContents());
2075 init_observer.SetPathToWaitFor("/done.html");
2076 scoped_ptr<PromptObserver> prompt_observer(
2077 PromptObserver::Create(WebContents()));
2078 std::string init_form = "sendMessage('init_fill');";
2079 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), init_form));
2080 init_observer.Wait();
2081 EXPECT_TRUE(prompt_observer->IsShowingPrompt());
2082 prompt_observer->Accept();
2083
2084 // Visit the form again
2085 NavigationObserver reload_observer(WebContents());
2086 NavigateToFile("/password/password_form_in_crosssite_iframe.html");
2087 reload_observer.Wait();
2088
2089 NavigationObserver ifrm_observer_2(WebContents());
2090 ifrm_observer_2.SetPathToWaitFor("/randomFile.html");
2091 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit));
2092 ifrm_observer_2.Wait();
2093
2094 // Verfiy username is not autofilled
2095 std::string empty_username;
2096 ASSERT_TRUE(content::ExecuteScriptAndExtractString(
2097 RenderViewHost(),
2098 "sendMessage('get_username');",
2099 &empty_username));
2100 ASSERT_EQ("", empty_username);
2101
2102
2103 // Verfiy password is not autofilled
2104 std::string empty_password;
2105 ASSERT_TRUE(content::ExecuteScriptAndExtractString(
2106 RenderViewHost(),
2107 "sendMessage('get_password');",
2108 &empty_password));
2109 ASSERT_EQ("", empty_password);
2110 }
2111
2112 // Check that a password form in an iframe of different origin will not be
2113 // filled in until a user interact with the form.
Garrett Casto 2015/05/26 20:31:37 This comment looks like it's from the other test.
2114 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
2115 SameOriginIframeAutoFillTest) {
2116 // Visit the sign-up form to store a password for autofill later
2117 NavigateToFile("/password/password_form_in_same_origin_iframe.html");
2118 NavigationObserver observer(WebContents());
2119 observer.SetPathToWaitFor("/password/done.html");
2120 scoped_ptr<PromptObserver> prompt_observer(
2121 PromptObserver::Create(WebContents()));
2122
2123 std::string submit =
2124 "var ifrmDoc = document.getElementById('iframe').contentDocument;"
2125 "ifrmDoc.getElementById('username_field').value = 'temp';"
2126 "ifrmDoc.getElementById('password_field').value = 'pa55w0rd';"
2127 "ifrmDoc.getElementById('input_submit_button').click();";
2128 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit));
2129 observer.Wait();
2130 EXPECT_TRUE(prompt_observer->IsShowingPrompt());
2131 prompt_observer->Accept();
2132
2133 // Visit the form again
2134 NavigationObserver reload_observer(WebContents());
2135 NavigateToFile("/password/password_form_in_same_origin_iframe.html");
2136 reload_observer.Wait();
2137
2138 // Verfiy username is autofilled
2139 CheckElementValue("iframe", "username_field", "temp");
2140
2141 // Verfiy password is not autofilled
2142 CheckElementValue("iframe", "password_field", "");
2143
2144 // Simulate the user interaction in the iframe which should trigger autofill.
2145 ASSERT_TRUE(content::ExecuteScript(
2146 RenderViewHost(),
2147 "var iframeRect = document.getElementById("
2148 "'iframe').getBoundingClientRect();"));
2149 int top;
2150 ASSERT_TRUE(content::ExecuteScriptAndExtractInt(
2151 RenderViewHost(),
2152 "window.domAutomationController.send(iframeRect.top);",
2153 &top));
2154 int left;
2155 ASSERT_TRUE(content::ExecuteScriptAndExtractInt(
2156 RenderViewHost(),
2157 "window.domAutomationController.send(iframeRect.left);",
2158 &left));
2159
2160 content::SimulateMouseClickAt(
2161 WebContents(), 0, blink::WebMouseEvent::ButtonLeft, gfx::Point(left + 1,
2162 top + 1));
2163 // Verify password has been autofilled
2164 WaitForElementValue("iframe", "password_field", "pa55w0rd");
2165
2166 // Verfiy username has been autofilled
2167 CheckElementValue("iframe", "username_field", "temp");
2168
2169 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698