Chromium Code Reviews| Index: chrome/browser/password_manager/password_manager_browsertest.cc |
| diff --git a/chrome/browser/password_manager/password_manager_browsertest.cc b/chrome/browser/password_manager/password_manager_browsertest.cc |
| index f759c2bb152c07a38ecf65c780b279a65185689a..383c25e36ad39d049b5f471c00b482783d035180 100644 |
| --- a/chrome/browser/password_manager/password_manager_browsertest.cc |
| +++ b/chrome/browser/password_manager/password_manager_browsertest.cc |
| @@ -48,6 +48,7 @@ |
| #include "content/public/test/browser_test_utils.h" |
| #include "content/public/test/test_utils.h" |
| #include "net/base/filename_util.h" |
| +#include "net/dns/mock_host_resolver.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| #include "net/test/embedded_test_server/http_request.h" |
| #include "net/test/embedded_test_server/http_response.h" |
| @@ -289,6 +290,29 @@ scoped_ptr<net::test_server::HttpResponse> HandleTestAuthRequest( |
| } |
| } |
| +// Handle |request| to arbitrary domain and respond with |response_content_| |
| +std::string response_content_; |
| +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
|
| + const net::test_server::HttpRequest& request) { |
| + scoped_ptr<net::test_server::BasicHttpResponse> http_response( |
| + new net::test_server::BasicHttpResponse()); |
| + http_response->set_code(net::HTTP_OK); |
| + http_response->set_content_type("text/html"); |
| + if (request.relative_url == "/done.html") { |
|
Garrett Casto
2015/05/26 20:31:37
Looks like this could be solved by having the path
|
| + std::string alt_response = |
| + "<html>" |
| + "<body>" |
| + "<a id='link' href='done.html'>Go somewhere</a>" |
| + "</body>" |
| + "</html>"; |
| + http_response->set_content(alt_response); |
| + } else { |
| + http_response->set_content(response_content_); |
| + } |
| + return http_response.Pass(); |
| +} |
| + |
| + |
| } // namespace |
| @@ -345,10 +369,18 @@ class PasswordManagerBrowserTest : public InProcessBrowserTest { |
| // event loop is spun to allow all other possible events to take place. |
| void WaitForElementValue(const std::string& element_id, |
| const std::string& expected_value); |
| + // Same as above except the element |element_id| is in iframe |iframe_id| |
| + void WaitForElementValue(const std::string& iframe_id, |
| + const std::string& element_id, |
| + const std::string& expected_value); |
| // Checks that the current "value" attribute of the HTML element with |
| // |element_id| is equal to |expected_value|. |
| void CheckElementValue(const std::string& element_id, |
| const std::string& expected_value); |
| + // Same as above except the element |element_id| is in iframe |iframe_id| |
| + void CheckElementValue(const std::string& iframe_id, |
| + const std::string& element_id, |
| + const std::string& expected_value); |
| // TODO(dvadym): Remove this once history.pushState() handling is not behind |
| // a flag. |
| @@ -366,6 +398,15 @@ class PasswordManagerBrowserTest : public InProcessBrowserTest { |
| void PasswordManagerBrowserTest::WaitForElementValue( |
| const std::string& element_id, |
| const std::string& expected_value) { |
| + PasswordManagerBrowserTest::WaitForElementValue("null", |
| + element_id, |
| + expected_value); |
| +} |
| + |
| +void PasswordManagerBrowserTest::WaitForElementValue( |
| + const std::string& iframe_id, |
| + const std::string& element_id, |
| + const std::string& expected_value) { |
| enum ReturnCodes { // Possible results of the JavaScript code. |
| RETURN_CODE_OK, |
| RETURN_CODE_NO_ELEMENT, |
| @@ -374,9 +415,17 @@ void PasswordManagerBrowserTest::WaitForElementValue( |
| }; |
| const std::string value_check_function = base::StringPrintf( |
| "function valueCheck() {" |
| - " var element = document.getElementById('%s');" |
| + " var element;" |
|
Garrett Casto
2015/05/26 20:31:37
This is a counter intuitive, but this line isn't n
|
| + " if (%s)" |
| + " var element = document.getElementById(" |
| + " '%s').contentDocument.getElementById('%s');" |
| + " else " |
| + " var element = document.getElementById('%s');" |
| " return element && element.value == '%s';" |
| "}", |
| + iframe_id.c_str(), |
| + iframe_id.c_str(), |
| + element_id.c_str(), |
| element_id.c_str(), |
| expected_value.c_str()); |
| const std::string script = |
| @@ -386,7 +435,12 @@ void PasswordManagerBrowserTest::WaitForElementValue( |
| " /* Spin the event loop with setTimeout. */" |
| " setTimeout(window.domAutomationController.send(%d), 0);" |
| "} else {" |
| - " var element = document.getElementById('%s');" |
| + " var element;" |
|
Garrett Casto
2015/05/26 20:31:37
Same here.
|
| + " if (%s)" |
| + " var element = document.getElementById(" |
| + " '%s').contentDocument.getElementById('%s');" |
| + " else " |
| + " var element = document.getElementById('%s');" |
| " if (!element)" |
| " window.domAutomationController.send(%d);" |
| " element.onchange = function() {" |
| @@ -399,6 +453,9 @@ void PasswordManagerBrowserTest::WaitForElementValue( |
| " };" |
| "}", |
| RETURN_CODE_OK, |
| + iframe_id.c_str(), |
| + iframe_id.c_str(), |
| + element_id.c_str(), |
| element_id.c_str(), |
| RETURN_CODE_NO_ELEMENT, |
| RETURN_CODE_OK, |
| @@ -414,9 +471,26 @@ void PasswordManagerBrowserTest::WaitForElementValue( |
| void PasswordManagerBrowserTest::CheckElementValue( |
| const std::string& element_id, |
| const std::string& expected_value) { |
| + PasswordManagerBrowserTest::CheckElementValue("null", |
| + element_id, |
| + expected_value); |
| +} |
| + |
| +void PasswordManagerBrowserTest::CheckElementValue( |
| + const std::string& iframe_id, |
| + const std::string& element_id, |
| + const std::string& expected_value) { |
| const std::string value_check_script = base::StringPrintf( |
| - "var element = document.getElementById('%s');" |
| + "var element;" |
|
Garrett Casto
2015/05/26 20:31:37
Same here.
|
| + "if (%s)" |
| + " var element = document.getElementById(" |
| + " '%s').contentDocument.getElementById('%s');" |
| + "else " |
| + " var element = document.getElementById('%s');" |
| "window.domAutomationController.send(element && element.value == '%s');", |
| + iframe_id.c_str(), |
| + iframe_id.c_str(), |
| + element_id.c_str(), |
| element_id.c_str(), |
| expected_value.c_str()); |
| bool return_value = false; |
| @@ -1913,3 +1987,183 @@ IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, BaseTagWithNoActionTest) { |
| // Wait until that interaction causes the password value to be revealed. |
| WaitForElementValue("password_field", "mypassword"); |
| } |
| + |
| +// Check that a password form in an iframe of different origin will not be |
| +// filled in until a user interact with the form. |
| +IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, CrossSiteIframeNotFillTest) { |
| + // Setup the mock host resolver |
| + host_resolver()->AddRule("*", "127.0.0.1"); |
| + response_content_ = "<html>" |
| + "<head>" |
| + " <meta charset='utf-8' />" |
| + " <title>Title</title>" |
| + "</head>" |
| + "<body id='inframe'>" |
| + "<script>" |
| + "function receiveMessage(event){" |
| + " if (event.data == 'init_fill') { " |
|
Garrett Casto
2015/05/26 20:31:37
Maybe this message should be "fill_and_submit" sin
|
| + " document.getElementById(" |
| + " 'username').value = 'temp';" |
| + " document.getElementById(" |
| + " 'password').value = 'pa55w0rd';" |
| + " document.getElementById('submit_button').click();" |
| + " } else if (event.data == 'get_username') {" |
| + " event.source.postMessage(" |
| + " document.getElementById('username').value," |
| + " event.origin);" |
| + " } else if (event.data == 'get_password') {" |
| + " event.source.postMessage(" |
| + " document.getElementById('password').value," |
| + " event.origin);" |
| + " } 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
|
| + " waitForValue('username', 'temp', event)" |
| + " } else if (event.data == 'check_for_password') {" |
| + " waitForValue('password', 'pa55w0rd', event)" |
| + " }" |
| + "}" |
| + "window.addEventListener('message'," |
| + " receiveMessage, false);" |
| + "" |
| + "function valueCheck(id, expect_value) {" |
|
Garrett Casto
2015/05/26 20:31:37
Looks like these two functions aren't necessary.
|
| + " var element = document.getElementById(id);" |
| + " return element && element.value == expect_value;" |
| + "}" |
| + "" |
| + "function waitForValue(id, expect_value, event){" |
| + " var element = document.getElementById(id);" |
| + " if (valueCheck(id, expect_value)) {" |
| + " /* Spin the event loop with setTimeout. */" |
| + " setTimeout(event.source.postMessage(" |
| + " element.value, event.origin), 0);" |
| + " } else {" |
| + " element.onchange = function() {" |
| + " if (valueCheck(id, expect_value)) {" |
| + " /* Spin the event loop with setTimeout. */" |
| + " setTimeout(event.source.postMessage(" |
| + " element.value, event.origin), 0);" |
| + " }" |
| + " };" |
| + " }" |
| + "}" |
| + "" |
| + "</script>" |
| + "<form method='POST' name='TestForm' action='done.html'>" |
| + " <input type='text' id='username'/>" |
| + " <input type='password' id='password'/>" |
| + " <input type='submit' id='submit_button' value='Log'/>" |
| + "</form>" |
| + "</body>" |
| + "</html>"; |
| + // Set up customized request handler |
| + embedded_test_server()->RegisterRequestHandler( |
| + base::Bind(&HandleTestRequest)); |
| + |
| + // 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
|
| + NavigateToFile("/password/password_form_in_crosssite_iframe.html"); |
| + NavigationObserver ifrm_observer(WebContents()); |
| + ifrm_observer.SetPathToWaitFor("/randomFile.html"); |
| + |
| + std::string submit = base::StringPrintf( |
|
Garrett Casto
2015/05/26 20:31:37
Nit: I'd name this string something more like "cre
|
| + "createCrossSiteIframe('http://randomsite.net:%d/randomFile.html');", |
| + embedded_test_server()->port()); |
| + |
| + ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit)); |
| + ifrm_observer.Wait(); |
| + ifrm_observer.SetPathToWaitFor(""); |
|
Garrett Casto
2015/05/26 20:31:37
This line looks superfluous as you change the stri
|
| + |
| + NavigationObserver init_observer(WebContents()); |
| + init_observer.SetPathToWaitFor("/done.html"); |
| + scoped_ptr<PromptObserver> prompt_observer( |
| + PromptObserver::Create(WebContents())); |
| + std::string init_form = "sendMessage('init_fill');"; |
| + ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), init_form)); |
| + init_observer.Wait(); |
| + EXPECT_TRUE(prompt_observer->IsShowingPrompt()); |
| + prompt_observer->Accept(); |
| + |
| + // Visit the form again |
| + NavigationObserver reload_observer(WebContents()); |
| + NavigateToFile("/password/password_form_in_crosssite_iframe.html"); |
| + reload_observer.Wait(); |
| + |
| + NavigationObserver ifrm_observer_2(WebContents()); |
| + ifrm_observer_2.SetPathToWaitFor("/randomFile.html"); |
| + ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit)); |
| + ifrm_observer_2.Wait(); |
| + |
| + // Verfiy username is not autofilled |
| + std::string empty_username; |
| + ASSERT_TRUE(content::ExecuteScriptAndExtractString( |
| + RenderViewHost(), |
| + "sendMessage('get_username');", |
| + &empty_username)); |
| + ASSERT_EQ("", empty_username); |
| + |
| + |
| + // Verfiy password is not autofilled |
| + std::string empty_password; |
| + ASSERT_TRUE(content::ExecuteScriptAndExtractString( |
| + RenderViewHost(), |
| + "sendMessage('get_password');", |
| + &empty_password)); |
| + ASSERT_EQ("", empty_password); |
| +} |
| + |
| +// Check that a password form in an iframe of different origin will not be |
| +// 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.
|
| +IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, |
| + SameOriginIframeAutoFillTest) { |
| + // Visit the sign-up form to store a password for autofill later |
| + NavigateToFile("/password/password_form_in_same_origin_iframe.html"); |
| + NavigationObserver observer(WebContents()); |
| + observer.SetPathToWaitFor("/password/done.html"); |
| + scoped_ptr<PromptObserver> prompt_observer( |
| + PromptObserver::Create(WebContents())); |
| + |
| + std::string submit = |
| + "var ifrmDoc = document.getElementById('iframe').contentDocument;" |
| + "ifrmDoc.getElementById('username_field').value = 'temp';" |
| + "ifrmDoc.getElementById('password_field').value = 'pa55w0rd';" |
| + "ifrmDoc.getElementById('input_submit_button').click();"; |
| + ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit)); |
| + observer.Wait(); |
| + EXPECT_TRUE(prompt_observer->IsShowingPrompt()); |
| + prompt_observer->Accept(); |
| + |
| + // Visit the form again |
| + NavigationObserver reload_observer(WebContents()); |
| + NavigateToFile("/password/password_form_in_same_origin_iframe.html"); |
| + reload_observer.Wait(); |
| + |
| + // Verfiy username is autofilled |
| + CheckElementValue("iframe", "username_field", "temp"); |
| + |
| + // Verfiy password is not autofilled |
| + CheckElementValue("iframe", "password_field", ""); |
| + |
| + // Simulate the user interaction in the iframe which should trigger autofill. |
| + ASSERT_TRUE(content::ExecuteScript( |
| + RenderViewHost(), |
| + "var iframeRect = document.getElementById(" |
| + "'iframe').getBoundingClientRect();")); |
| + int top; |
| + ASSERT_TRUE(content::ExecuteScriptAndExtractInt( |
| + RenderViewHost(), |
| + "window.domAutomationController.send(iframeRect.top);", |
| + &top)); |
| + int left; |
| + ASSERT_TRUE(content::ExecuteScriptAndExtractInt( |
| + RenderViewHost(), |
| + "window.domAutomationController.send(iframeRect.left);", |
| + &left)); |
| + |
| + content::SimulateMouseClickAt( |
| + WebContents(), 0, blink::WebMouseEvent::ButtonLeft, gfx::Point(left + 1, |
| + top + 1)); |
| + // Verify password has been autofilled |
| + WaitForElementValue("iframe", "password_field", "pa55w0rd"); |
| + |
| + // Verfiy username has been autofilled |
| + CheckElementValue("iframe", "username_field", "temp"); |
| + |
| +} |