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..ac8eac489fb8d5f341a6e334e7497bde66ec3820 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" |
@@ -345,10 +346,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 +375,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 +392,16 @@ void PasswordManagerBrowserTest::WaitForElementValue( |
}; |
const std::string value_check_function = base::StringPrintf( |
"function valueCheck() {" |
- " var element = document.getElementById('%s');" |
+ " 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 +411,11 @@ void PasswordManagerBrowserTest::WaitForElementValue( |
" /* Spin the event loop with setTimeout. */" |
" setTimeout(window.domAutomationController.send(%d), 0);" |
"} else {" |
- " var element = document.getElementById('%s');" |
+ " 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 +428,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 +446,25 @@ 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');" |
+ "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 +1961,153 @@ 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"); |
+ |
+ // Here we need to dynamically create the iframe because the port |
+ // embedded_test_server ran on was dynamically allocated, so the iframe's src |
+ // attribute can only be determined at run time. |
+ NavigateToFile("/password/password_form_in_crosssite_iframe.html"); |
+ NavigationObserver ifrm_observer(WebContents()); |
+ ifrm_observer.SetPathToWaitFor("/password/crossite_iframe_content.html"); |
+ std::string create_iframe = base::StringPrintf( |
+ "create_iframe(" |
+ "'http://randomsite.net:%d/password/crossite_iframe_content.html');", |
+ embedded_test_server()->port()); |
+ ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), create_iframe)); |
+ ifrm_observer.Wait(); |
+ |
+ // Store a password for autofill later |
+ NavigationObserver init_observer(WebContents()); |
+ init_observer.SetPathToWaitFor("/password/done.html"); |
+ scoped_ptr<PromptObserver> prompt_observer( |
+ PromptObserver::Create(WebContents())); |
+ std::string init_form = "sendMessage('fill_and_submit');"; |
+ 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("/password/crossite_iframe_content.html"); |
+ ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), create_iframe)); |
+ ifrm_observer_2.Wait(); |
+ |
+ // Verify username is not autofilled |
+ std::string empty_username; |
+ ASSERT_TRUE(content::ExecuteScriptAndExtractString( |
+ RenderViewHost(), |
+ "sendMessage('get_username');", |
+ &empty_username)); |
+ ASSERT_EQ("", empty_username); |
+ // Verify password is not autofilled |
+ std::string empty_password; |
+ ASSERT_TRUE(content::ExecuteScriptAndExtractString( |
+ RenderViewHost(), |
+ "sendMessage('get_password');", |
+ &empty_password)); |
+ ASSERT_EQ("", empty_password); |
+ |
+ // Simulate the user interaction in the iframe and verify autofill is not |
+ // triggered. Note this check is only best-effort because we don't know how |
+ // long to wait before we are certain that no autofill will be triggered. |
+ // Theoretically unexpected autofill can happen after this check. |
+ 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 username is not autofilled |
+ ASSERT_TRUE(content::ExecuteScriptAndExtractString( |
+ RenderViewHost(), |
+ "sendMessage('get_username');", |
+ &empty_username)); |
+ ASSERT_EQ("", empty_username); |
+ // Verify password is not autofilled |
+ ASSERT_TRUE(content::ExecuteScriptAndExtractString( |
+ RenderViewHost(), |
+ "sendMessage('get_password');", |
+ &empty_password)); |
+ ASSERT_EQ("", empty_password); |
+} |
+ |
+// Check that a password form in an iframe of same origin will not be |
+// filled in until user interact with the iframe. |
+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(); |
+ |
+ // Verify username is autofilled |
+ CheckElementValue("iframe", "username_field", "temp"); |
+ |
+ // Verify 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"); |
+ |
+ // Verify username has been autofilled |
+ CheckElementValue("iframe", "username_field", "temp"); |
+ |
+} |