Merge "cros: Support dynamic password field for SAML."
> - Add MutationObserver to handle dynamically added password fields;
> - Remove unnecessary default text css;
> - Put channel.js before background.js to fix the unknown Channel error;
>
> BUG=447655
> TEST=SamlTest.ScrapedDynamic
>
> Review URL: https://codereview.chromium.org/844083003
>
> Cr-Commit-Position: refs/heads/master@{#311099}
> (cherry picked from commit da0943f77322c319a724c5f14cbeb0952ca98d79)
BUG=458220
TBR=xiyuan@chromium.org
Review URL: https://codereview.chromium.org/921313002
Cr-Commit-Position: refs/branch-heads/2272@{#295}
Cr-Branched-From: 827a380cfdb31aa54c8d56e63ce2c3fd8c3ba4d4-refs/heads/master@{#310958}
diff --git a/chrome/browser/chromeos/login/saml/saml_browsertest.cc b/chrome/browser/chromeos/login/saml/saml_browsertest.cc
index f28c1d7..3e09228 100644
--- a/chrome/browser/chromeos/login/saml/saml_browsertest.cc
+++ b/chrome/browser/chromeos/login/saml/saml_browsertest.cc
@@ -509,11 +509,44 @@
// Lands on confirm password screen.
OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait();
- // Enter an unknown password should go back to confirm password screen.
+ // Entering an unknown password should go back to the confirm password screen.
SendConfirmPassword("wrong_password");
OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait();
- // Enter a known password should finish login and start session.
+ // Entering a known password should finish login and start session.
+ SendConfirmPassword("fake_password");
+ content::WindowedNotificationObserver(
+ chrome::NOTIFICATION_SESSION_STARTED,
+ content::NotificationService::AllSources()).Wait();
+}
+
+// Tests password scraping from a dynamically created password field.
+IN_PROC_BROWSER_TEST_F(SamlTest, ScrapedDynamic) {
+ fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
+ StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
+
+ ExecuteJsInSigninFrame(
+ "(function() {"
+ "var newPassInput = document.createElement('input');"
+ "newPassInput.id = 'DynamicallyCreatedPassword';"
+ "newPassInput.type = 'password';"
+ "newPassInput.name = 'Password';"
+ "document.forms[0].appendChild(newPassInput);"
+ "})();");
+
+ // Fill-in the SAML IdP form and submit.
+ SetSignFormField("Email", "fake_user");
+ SetSignFormField("DynamicallyCreatedPassword", "fake_password");
+ ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
+
+ // Lands on confirm password screen.
+ OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait();
+
+ // Entering an unknown password should go back to the confirm password screen.
+ SendConfirmPassword("wrong_password");
+ OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait();
+
+ // Entering a known password should finish login and start session.
SendConfirmPassword("fake_password");
content::WindowedNotificationObserver(
chrome::NOTIFICATION_SESSION_STARTED,
diff --git a/chrome/browser/resources/gaia_auth/main.html b/chrome/browser/resources/gaia_auth/main.html
index 421364d..20d079ea 100644
--- a/chrome/browser/resources/gaia_auth/main.html
+++ b/chrome/browser/resources/gaia_auth/main.html
@@ -1,7 +1,6 @@
<!doctype html>
<html>
<head>
- <link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
<link rel="stylesheet" href="main.css">
<meta charset="utf-8">
<script src="channel.js"></script>
diff --git a/chrome/browser/resources/gaia_auth/manifest.json b/chrome/browser/resources/gaia_auth/manifest.json
index 7295c01..2281ee14 100644
--- a/chrome/browser/resources/gaia_auth/manifest.json
+++ b/chrome/browser/resources/gaia_auth/manifest.json
@@ -5,7 +5,7 @@
"version": "0.0.1",
"manifest_version": 2,
"background" : {
- "scripts": ["background.js", "channel.js"]
+ "scripts": ["channel.js", "background.js"]
},
"content_scripts": [
{
diff --git a/chrome/browser/resources/gaia_auth/manifest_keyboard.json b/chrome/browser/resources/gaia_auth/manifest_keyboard.json
index 68d59a1..63d07a19 100644
--- a/chrome/browser/resources/gaia_auth/manifest_keyboard.json
+++ b/chrome/browser/resources/gaia_auth/manifest_keyboard.json
@@ -5,7 +5,7 @@
"version": "0.0.1",
"manifest_version": 2,
"background" : {
- "scripts": ["background.js", "channel.js"]
+ "scripts": ["channel.js", "background.js"]
},
"content_scripts": [
{
diff --git a/chrome/browser/resources/gaia_auth/offline.html b/chrome/browser/resources/gaia_auth/offline.html
index 43ed5794..a9afb7c 100644
--- a/chrome/browser/resources/gaia_auth/offline.html
+++ b/chrome/browser/resources/gaia_auth/offline.html
@@ -2,7 +2,6 @@
<html>
<head>
<meta charset="utf-8">
- <link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
<link rel="stylesheet" href="offline.css">
<script src="util.js"></script>
<script src="offline.js"></script>
diff --git a/chrome/browser/resources/gaia_auth/saml_injected.js b/chrome/browser/resources/gaia_auth/saml_injected.js
index fd08bd5..fd57600 100644
--- a/chrome/browser/resources/gaia_auth/saml_injected.js
+++ b/chrome/browser/resources/gaia_auth/saml_injected.js
@@ -78,6 +78,9 @@
// An array to hold cached password values.
passwordValues_: null,
+ // A MutationObserver to watch for dynamic password field creation.
+ passwordFieldsObserver: null,
+
/**
* Initialize the scraper with given channel and docRoot. Note that the
* scanning for password fields happens inside the function and does not
@@ -91,15 +94,59 @@
this.pageURL_ = pageURL;
this.channel_ = channel;
- this.passwordFields_ = docRoot.querySelectorAll('input[type=password]');
+ this.passwordFields_ = [];
this.passwordValues_ = [];
- for (var i = 0; i < this.passwordFields_.length; ++i) {
- this.passwordFields_[i].addEventListener(
- 'input', this.onPasswordChanged_.bind(this, i));
+ this.findAndTrackChildren(docRoot);
- this.passwordValues_[i] = this.passwordFields_[i].value;
- }
+ this.passwordFieldsObserver = new MutationObserver(function(mutations) {
+ mutations.forEach(function(mutation) {
+ Array.prototype.forEach.call(
+ mutation.addedNodes,
+ function(addedNode) {
+ if (addedNode.nodeType != Node.ELEMENT_NODE)
+ return;
+
+ if (addedNode.matches('input[type=password]')) {
+ this.trackPasswordField(addedNode);
+ } else {
+ this.findAndTrackChildren(addedNode);
+ }
+ }.bind(this));
+ }.bind(this));
+ }.bind(this));
+ this.passwordFieldsObserver.observe(docRoot,
+ {subtree: true, childList: true});
+ },
+
+ /**
+ * Find and track password fields that are descendants of the given element.
+ * @param {!HTMLElement} element The parent element to search from.
+ */
+ findAndTrackChildren: function(element) {
+ Array.prototype.forEach.call(
+ element.querySelectorAll('input[type=password]'), function(field) {
+ this.trackPasswordField(field);
+ }.bind(this));
+ },
+
+ /**
+ * Start tracking value changes of the given password field if it is
+ * not being tracked yet.
+ * @param {!HTMLInputElement} passworField The password field to track.
+ */
+ trackPasswordField: function(passwordField) {
+ var existing = this.passwordFields_.filter(function(element) {
+ return element === passwordField;
+ });
+ if (existing.length != 0)
+ return;
+
+ var index = this.passwordFields_.length;
+ passwordField.addEventListener(
+ 'input', this.onPasswordChanged_.bind(this, index));
+ this.passwordFields_.push(passwordField);
+ this.passwordValues_.push(passwordField.value);
},
/**
@@ -143,13 +190,25 @@
var apiCallForwarder = new APICallForwarder();
apiCallForwarder.init(channel);
- var passwordScraper = new PasswordInputScraper();
- passwordScraper.init(channel, pageURL, document.documentElement);
+ var initPasswordScraper = function() {
+ var passwordScraper = new PasswordInputScraper();
+ passwordScraper.init(channel, pageURL, document.documentElement);
+ };
+
+ if (document.readyState == 'loading') {
+ window.addEventListener('readystatechange', function listener(event) {
+ if (document.readyState == 'loading')
+ return;
+ initPasswordScraper();
+ window.removeEventListener(event.type, listener, true);
+ }, true);
+ } else {
+ initPasswordScraper();
+ }
}
var channel = new Channel();
channel.connect('injected');
channel.sendWithCallback({name: 'getSAMLFlag'},
onGetSAMLFlag.bind(undefined, channel));
-
})();
diff --git a/chrome/browser/resources/gaia_auth/success.html b/chrome/browser/resources/gaia_auth/success.html
index 240e531..bdbf41a6 100644
--- a/chrome/browser/resources/gaia_auth/success.html
+++ b/chrome/browser/resources/gaia_auth/success.html
@@ -1,7 +1,6 @@
<!doctype html>
<head>
<meta charset="utf-8">
-<link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
</head>
<html>
<body></body>