summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorgcasto@chromium.org <gcasto@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-18 21:54:24 +0000
committergcasto@chromium.org <gcasto@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-18 21:54:24 +0000
commit2cd51863e9b7b425f877a9d309babe652e27192a (patch)
treeaf768e6f2a6463c70fd04f5adc2a78461709ac53
parent557c7bd3a12d45a4aeb1ced28876831f845ed8c0 (diff)
downloadchromium_src-2cd51863e9b7b425f877a9d309babe652e27192a.zip
chromium_src-2cd51863e9b7b425f877a9d309babe652e27192a.tar.gz
chromium_src-2cd51863e9b7b425f877a9d309babe652e27192a.tar.bz2
Revert "Revert 223907 "[password generation] Upload possible account cre...""
Re-commit change. Update to not use <iostream> to follow. BUG=240559 Review URL: https://codereview.chromium.org/23857010 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@223948 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/password_manager/login_database.cc44
-rw-r--r--chrome/browser/password_manager/login_database_unittest.cc16
-rw-r--r--chrome/browser/password_manager/password_form_data.cc32
-rw-r--r--chrome/browser/password_manager/password_form_data.h7
-rw-r--r--chrome/browser/password_manager/password_form_manager.cc31
-rw-r--r--chrome/browser/password_manager/password_form_manager.h6
-rw-r--r--chrome/browser/password_manager/password_manager_browsertest.cc64
-rw-r--r--chrome/test/data/password/password_form.html2
-rw-r--r--chrome/test/data/password/signup_form.html20
-rw-r--r--components/autofill/content/renderer/password_form_conversion_utils.cc11
-rw-r--r--components/autofill/core/browser/autofill_manager.cc72
-rw-r--r--components/autofill/core/browser/autofill_manager.h13
-rw-r--r--components/autofill/core/browser/form_structure.h3
-rw-r--r--components/autofill/core/common/autofill_messages.h9
-rw-r--r--components/autofill/core/common/autofill_param_traits_macros.h10
-rw-r--r--components/autofill/core/common/form_data.cc14
-rw-r--r--components/autofill/core/common/form_data.h5
-rw-r--r--components/autofill/core/common/password_form.cc53
-rw-r--r--components/autofill/core/common/password_form.h16
-rw-r--r--tools/metrics/histograms/histograms.xml10
20 files changed, 364 insertions, 74 deletions
diff --git a/chrome/browser/password_manager/login_database.cc b/chrome/browser/password_manager/login_database.cc
index 906ee7b..929f7cd 100644
--- a/chrome/browser/password_manager/login_database.cc
+++ b/chrome/browser/password_manager/login_database.cc
@@ -22,7 +22,7 @@
using autofill::PasswordForm;
-static const int kCurrentVersionNumber = 3;
+static const int kCurrentVersionNumber = 4;
static const int kCompatibleVersionNumber = 1;
namespace {
@@ -44,7 +44,8 @@ enum LoginTableColumns {
COLUMN_SCHEME,
COLUMN_PASSWORD_TYPE,
COLUMN_POSSIBLE_USERNAMES,
- COLUMN_TIMES_USED
+ COLUMN_TIMES_USED,
+ COLUMN_FORM_DATA
};
// Using the public suffix list for matching the origin is only needed for
@@ -184,16 +185,20 @@ bool LoginDatabase::MigrateOldVersionsAsNeeded() {
"ADD COLUMN possible_usernames BLOB")) {
return false;
}
+ // Fall through.
case 2:
- if (!db_.Execute("ALTER TABLE logins "
- "ADD COLUMN times_used INTEGER")) {
+ if (!db_.Execute("ALTER TABLE logins ADD COLUMN times_used INTEGER")) {
+ return false;
+ }
+ // Fall through.
+ case 3:
+ if (!db_.Execute("ALTER TABLE logins ADD COLUMN form_data BLOB")) {
return false;
}
- break;
+ // Fall through.
case kCurrentVersionNumber:
// Already up to date
return true;
- break;
default:
NOTREACHED();
return false;
@@ -221,6 +226,7 @@ bool LoginDatabase::InitLoginsTable() {
"password_type INTEGER,"
"possible_usernames BLOB,"
"times_used INTEGER,"
+ "form_data BLOB,"
"UNIQUE "
"(origin_url, username_element, "
"username_value, password_element, "
@@ -298,9 +304,9 @@ bool LoginDatabase::AddLogin(const PasswordForm& form) {
"(origin_url, action_url, username_element, username_value, "
" password_element, password_value, submit_element, "
" signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, "
- " scheme, password_type, possible_usernames, times_used) "
+ " scheme, password_type, possible_usernames, times_used, form_data) "
"VALUES "
- "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
+ "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
s.BindString(COLUMN_ORIGIN_URL, form.origin.spec());
s.BindString(COLUMN_ACTION_URL, form.action.spec());
s.BindString16(COLUMN_USERNAME_ELEMENT, form.username_element);
@@ -316,9 +322,16 @@ bool LoginDatabase::AddLogin(const PasswordForm& form) {
s.BindInt(COLUMN_BLACKLISTED_BY_USER, form.blacklisted_by_user);
s.BindInt(COLUMN_SCHEME, form.scheme);
s.BindInt(COLUMN_PASSWORD_TYPE, form.type);
- Pickle pickle = SerializeVector(form.other_possible_usernames);
- s.BindBlob(COLUMN_POSSIBLE_USERNAMES, pickle.data(), pickle.size());
+ Pickle usernames_pickle = SerializeVector(form.other_possible_usernames);
+ s.BindBlob(COLUMN_POSSIBLE_USERNAMES,
+ usernames_pickle.data(),
+ usernames_pickle.size());
s.BindInt(COLUMN_TIMES_USED, form.times_used);
+ Pickle form_data_pickle;
+ autofill::SerializeFormData(form.form_data, &form_data_pickle);
+ s.BindBlob(COLUMN_FORM_DATA,
+ form_data_pickle.data(),
+ form_data_pickle.size());
return s.Run();
}
@@ -431,6 +444,11 @@ bool LoginDatabase::InitPasswordFormFromStatement(PasswordForm* form,
s.ColumnByteLength(COLUMN_POSSIBLE_USERNAMES));
form->other_possible_usernames = DeserializeVector(pickle);
form->times_used = s.ColumnInt(COLUMN_TIMES_USED);
+ Pickle form_data_pickle(
+ static_cast<const char*>(s.ColumnBlob(COLUMN_FORM_DATA)),
+ s.ColumnByteLength(COLUMN_FORM_DATA));
+ PickleIterator form_data_iter(form_data_pickle);
+ autofill::DeserializeFormData(&form_data_iter, &form->form_data);
return true;
}
@@ -442,7 +460,7 @@ bool LoginDatabase::GetLogins(const PasswordForm& form,
"username_element, username_value, "
"password_element, password_value, submit_element, "
"signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, "
- "scheme, password_type, possible_usernames, times_used "
+ "scheme, password_type, possible_usernames, times_used, form_data "
"FROM logins WHERE signon_realm == ? ";
sql::Statement s;
const GURL signon_realm(form.signon_realm);
@@ -521,7 +539,7 @@ bool LoginDatabase::GetLoginsCreatedBetween(
"username_element, username_value, "
"password_element, password_value, submit_element, "
"signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, "
- "scheme, password_type, possible_usernames, times_used "
+ "scheme, password_type, possible_usernames, times_used, form_data "
"FROM logins WHERE date_created >= ? AND date_created < ?"
"ORDER BY origin_url"));
s.BindInt64(0, begin.ToTimeT());
@@ -556,7 +574,7 @@ bool LoginDatabase::GetAllLoginsWithBlacklistSetting(
"username_element, username_value, "
"password_element, password_value, submit_element, "
"signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, "
- "scheme, password_type, possible_usernames, times_used "
+ "scheme, password_type, possible_usernames, times_used, form_data "
"FROM logins WHERE blacklisted_by_user == ? "
"ORDER BY origin_url"));
s.BindInt(0, blacklisted ? 1 : 0);
diff --git a/chrome/browser/password_manager/login_database_unittest.cc b/chrome/browser/password_manager/login_database_unittest.cc
index 7b062a4..98bef92 100644
--- a/chrome/browser/password_manager/login_database_unittest.cc
+++ b/chrome/browser/password_manager/login_database_unittest.cc
@@ -41,6 +41,15 @@ class LoginDatabaseTest : public testing::Test {
db_.public_suffix_domain_matching_ = enabled;
}
+ void FormsAreEqual(const PasswordForm& expected, const PasswordForm& actual) {
+ PasswordForm expected_copy(expected);
+#if defined(OS_MACOSX)
+ // On the Mac we should never be storing passwords in the database.
+ expected_copy.password_value = ASCIIToUTF16("");
+#endif
+ EXPECT_EQ(expected_copy, actual);
+ }
+
base::ScopedTempDir temp_dir_;
base::FilePath file_;
LoginDatabase db_;
@@ -66,11 +75,16 @@ TEST_F(LoginDatabaseTest, Logins) {
form.ssl_valid = false;
form.preferred = false;
form.scheme = PasswordForm::SCHEME_HTML;
+ form.times_used = 1;
+ form.form_data.name = ASCIIToUTF16("form_name");
+ form.form_data.method = ASCIIToUTF16("POST");
- // Add it and make sure it is there.
+ // Add it and make sure it is there and that all the fields were retrieved
+ // correctly.
EXPECT_TRUE(db_.AddLogin(form));
EXPECT_TRUE(db_.GetAutofillableLogins(&result));
EXPECT_EQ(1U, result.size());
+ FormsAreEqual(form, *result[0]);
delete result[0];
result.clear();
diff --git a/chrome/browser/password_manager/password_form_data.cc b/chrome/browser/password_manager/password_form_data.cc
index 458de6f..14beef6 100644
--- a/chrome/browser/password_manager/password_form_data.cc
+++ b/chrome/browser/password_manager/password_form_data.cc
@@ -37,38 +37,6 @@ PasswordForm* CreatePasswordFormFromData(
return form;
}
-bool operator==(const PasswordForm& lhs, const PasswordForm& rhs) {
- return (lhs.scheme == rhs.scheme &&
- lhs.signon_realm == rhs.signon_realm &&
- lhs.origin == rhs.origin &&
- lhs.action == rhs.action &&
- lhs.submit_element == rhs.submit_element &&
- lhs.username_element == rhs.username_element &&
- lhs.password_element == rhs.password_element &&
- lhs.username_value == rhs.username_value &&
- lhs.password_value == rhs.password_value &&
- lhs.blacklisted_by_user == rhs.blacklisted_by_user &&
- lhs.preferred == rhs.preferred &&
- lhs.ssl_valid == rhs.ssl_valid &&
- lhs.date_created == rhs.date_created);
-}
-
-std::ostream& operator<<(std::ostream& os, const PasswordForm& form) {
- return os << "scheme: " << form.scheme << std::endl
- << "signon_realm: " << form.signon_realm << std::endl
- << "origin: " << form.origin << std::endl
- << "action: " << form.action << std::endl
- << "submit_element: " << form.submit_element << std::endl
- << "username_elem: " << form.username_element << std::endl
- << "password_elem: " << form.password_element << std::endl
- << "username_value: " << form.username_value << std::endl
- << "password_value: " << form.password_value << std::endl
- << "blacklisted: " << form.blacklisted_by_user << std::endl
- << "preferred: " << form.preferred << std::endl
- << "ssl_valid: " << form.ssl_valid << std::endl
- << "date_created: " << form.date_created.ToDoubleT();
-}
-
typedef std::set<const autofill::PasswordForm*> SetOfForms;
bool ContainsSamePasswordFormsPtr(
diff --git a/chrome/browser/password_manager/password_form_data.h b/chrome/browser/password_manager/password_form_data.h
index fef54a1..0421a7d 100644
--- a/chrome/browser/password_manager/password_form_data.h
+++ b/chrome/browser/password_manager/password_form_data.h
@@ -10,6 +10,8 @@
#include "components/autofill/core/common/password_form.h"
#include "testing/gmock/include/gmock/gmock.h"
+// TODO(sync): This file must eventually be refactored away -- crbug.com/87185.
+
// Struct used for creation of PasswordForms from static arrays of data.
// Note: This is only meant to be used in unit test.
struct PasswordFormData {
@@ -42,11 +44,6 @@ bool ContainsSamePasswordForms(
std::vector<autofill::PasswordForm>& first,
std::vector<autofill::PasswordForm>& second);
-// Pretty-prints the contents of a PasswordForm.
-// TODO(sync): This file must eventually be refactored away -- crbug.com/87185.
-std::ostream& operator<<(std::ostream& os,
- const autofill::PasswordForm& form);
-
// This gmock matcher is used to check that the |arg| contains exactly the same
// PasswordForms as |forms|, regardless of order.
MATCHER_P(ContainsAllPasswordForms, forms, "") {
diff --git a/chrome/browser/password_manager/password_form_manager.cc b/chrome/browser/password_manager/password_form_manager.cc
index fc3ddc2..b28ba21 100644
--- a/chrome/browser/password_manager/password_form_manager.cc
+++ b/chrome/browser/password_manager/password_form_manager.cc
@@ -13,12 +13,15 @@
#include "chrome/browser/password_manager/password_store.h"
#include "chrome/browser/password_manager/password_store_factory.h"
#include "chrome/browser/profiles/profile.h"
+#include "components/autofill/content/browser/autofill_driver_impl.h"
+#include "components/autofill/core/browser/form_structure.h"
#include "components/autofill/core/browser/validation.h"
#include "components/autofill/core/common/autofill_messages.h"
#include "components/autofill/core/common/password_form.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
+using autofill::FormStructure;
using autofill::PasswordForm;
using autofill::PasswordFormMap;
using base::Time;
@@ -486,6 +489,9 @@ void PasswordFormManager::UpdateLogin() {
// Update metadata.
++pending_credentials_.times_used;
+ // Check to see if this form is a candidate for password generation.
+ CheckForAccountCreationForm(pending_credentials_, observed_form_);
+
UpdatePreferredLoginState(password_store);
// Remove alternate usernames. At this point we assume that we have found
@@ -543,6 +549,31 @@ bool PasswordFormManager::UpdatePendingCredentialsIfOtherPossibleUsername(
return false;
}
+void PasswordFormManager::CheckForAccountCreationForm(
+ const PasswordForm& pending, const PasswordForm& observed) {
+ // We check to see if the saved form_data is the same as the observed
+ // form_data, which should never be true for passwords saved on account
+ // creation forms. This check is only made the first time a password is used
+ // to cut down on false positives. Specifically a site may have multiple login
+ // forms with different markup, which might look similar to a signup form.
+ if (pending.times_used == 1) {
+ FormStructure pending_structure(pending.form_data);
+ FormStructure observed_structure(observed.form_data);
+ if (pending_structure.FormSignature() !=
+ observed_structure.FormSignature()) {
+ autofill::AutofillDriverImpl* driver =
+ autofill::AutofillDriverImpl::FromWebContents(web_contents_);
+ if (driver && driver->autofill_manager()) {
+ // Note that this doesn't guarantee that the upload succeeded, only that
+ // |pending.form_data| is considered uploadable.
+ bool success = driver->autofill_manager()->UploadPasswordGenerationForm(
+ pending.form_data);
+ UMA_HISTOGRAM_BOOLEAN("PasswordGeneration.UploadStarted", success);
+ }
+ }
+ }
+}
+
int PasswordFormManager::ScoreResult(const PasswordForm& candidate) const {
DCHECK_EQ(state_, MATCHING_PHASE);
// For scoring of candidate login data:
diff --git a/chrome/browser/password_manager/password_form_manager.h b/chrome/browser/password_manager/password_form_manager.h
index 064849f..9efc135 100644
--- a/chrome/browser/password_manager/password_form_manager.h
+++ b/chrome/browser/password_manager/password_form_manager.h
@@ -217,6 +217,12 @@ class PasswordFormManager : public PasswordStoreConsumer {
// is now implicitly 'preferred'.
void UpdateLogin();
+ // Check to see if |pending| corresponds to an account creation form. If we
+ // think that it does, we label it as such and upload this state to the
+ // Autofill server, so that we will trigger password generation in the future.
+ void CheckForAccountCreationForm(const autofill::PasswordForm& pending,
+ const autofill::PasswordForm& observed);
+
// Update all login matches to reflect new preferred state - preferred flag
// will be reset on all matched logins that different than the current
// |pending_credentials_|.
diff --git a/chrome/browser/password_manager/password_manager_browsertest.cc b/chrome/browser/password_manager/password_manager_browsertest.cc
index 54ecfe6..be0c84c 100644
--- a/chrome/browser/password_manager/password_manager_browsertest.cc
+++ b/chrome/browser/password_manager/password_manager_browsertest.cc
@@ -5,6 +5,8 @@
#include <string>
#include "base/command_line.h"
+#include "base/metrics/histogram_samples.h"
+#include "base/metrics/statistics_recorder.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/infobars/confirm_infobar_delegate.h"
#include "chrome/browser/infobars/infobar_service.h"
@@ -15,6 +17,7 @@
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/test_switches.h"
#include "chrome/test/base/ui_test_utils.h"
+#include "components/autofill/core/browser/autofill_common_test.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
#include "content/public/browser/notification_service.h"
@@ -24,6 +27,7 @@
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_utils.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/url_request/test_url_fetcher_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "ui/events/keycodes/keyboard_codes.h"
@@ -130,7 +134,8 @@ class PasswordManagerBrowserTest : public InProcessBrowserTest {
// would sometimes see the DidFinishLoad event from a previous navigation and
// return immediately.
void NavigateToFile(const std::string& path) {
- ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+ if (!embedded_test_server()->Started())
+ ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
NavigationObserver observer(WebContents());
GURL url = embedded_test_server()->GetURL(path);
@@ -292,3 +297,60 @@ IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, NoPromptForOtherXHR) {
observer.Wait();
EXPECT_FALSE(observer.infobar_shown());
}
+
+IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
+ VerifyPasswordGenerationUpload) {
+ // Prevent Autofill requests from actually going over the wire.
+ net::TestURLFetcherFactory factory;
+ // Disable Autofill requesting access to AddressBook data. This causes
+ // the test to hang on Mac.
+ autofill::test::DisableSystemServices(browser()->profile());
+
+ // Visit a signup form.
+ NavigateToFile("/password/signup_form.html");
+
+ // Enter a password and save it.
+ NavigationObserver first_observer(WebContents());
+ std::string fill_and_submit =
+ "document.getElementById('other_info').value = 'stuff';"
+ "document.getElementById('username_field').value = 'my_username';"
+ "document.getElementById('password_field').value = 'password';"
+ "document.getElementById('input_submit_button').click()";
+ ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
+
+ first_observer.Wait();
+ ASSERT_TRUE(first_observer.infobar_shown());
+
+ // Now navigate to a login form that has similar HTML markup.
+ NavigateToFile("/password/password_form.html");
+
+ // The form should be filled with the previously submitted username.
+ std::string get_username =
+ "window.domAutomationController.send("
+ "document.getElementById('username_field').value);";
+ std::string actual_username;
+ ASSERT_TRUE(content::ExecuteScriptAndExtractString(RenderViewHost(),
+ get_username,
+ &actual_username));
+ ASSERT_EQ("my_username", actual_username);
+
+ // Submit the form and verify that there is no infobar (as the password
+ // has already been saved).
+ NavigationObserver second_observer(WebContents());
+ std::string submit_form =
+ "document.getElementById('input_submit_button').click()";
+ ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit_form));
+ second_observer.Wait();
+ EXPECT_FALSE(second_observer.infobar_shown());
+
+ // Verify that we sent a ping to Autofill saying that the original form
+ // was likely an account creation form since it has more than 2 text input
+ // fields and was used for the first time on a different form.
+ base::HistogramBase* upload_histogram =
+ base::StatisticsRecorder::FindHistogram(
+ "PasswordGeneration.UploadStarted");
+ scoped_ptr<base::HistogramSamples> snapshot =
+ upload_histogram->SnapshotSamples();
+ EXPECT_EQ(0, snapshot->GetCount(0 /* failure */));
+ EXPECT_EQ(1, snapshot->GetCount(1 /* success */));
+}
diff --git a/chrome/test/data/password/password_form.html b/chrome/test/data/password/password_form.html
index ac96b5b..7c8551d 100644
--- a/chrome/test/data/password/password_form.html
+++ b/chrome/test/data/password/password_form.html
@@ -1,6 +1,6 @@
<html>
<body>
-<form action="done.html" onsubmit="return true;" id="testform">
+<form method="POST" action="done.html" onsubmit="return true;" id="testform">
<input type="text" id="username_field" name="username_field">
<input type="password" id="password_field" name="password_field">
<input type="submit" id="input_submit_button" name="input_submit_button">
diff --git a/chrome/test/data/password/signup_form.html b/chrome/test/data/password/signup_form.html
new file mode 100644
index 0000000..abdac71
--- /dev/null
+++ b/chrome/test/data/password/signup_form.html
@@ -0,0 +1,20 @@
+<!--
+ A mock signup form. Essentially the same as password_form.html, but includes
+ an additional text field which will cause it to be treated differently.
+ -->
+<html>
+<body>
+<form method="POST" action="done.html" onsubmit="return true;" id="testform">
+ <input type="text" id="other_info" name="other_info">
+ <input type="text" id="username_field" name="username_field">
+ <input type="password" id="password_field" name="password_field">
+ <input type="submit" id="input_submit_button" name="input_submit_button">
+</form>
+
+<button id="submit_button" name="submit_button"
+ onclick="document.getElementById('testform').submit()">
+ Submit!
+</button>
+
+</body>
+</html>
diff --git a/components/autofill/content/renderer/password_form_conversion_utils.cc b/components/autofill/content/renderer/password_form_conversion_utils.cc
index 3066373..7dfd45d 100644
--- a/components/autofill/content/renderer/password_form_conversion_utils.cc
+++ b/components/autofill/content/renderer/password_form_conversion_utils.cc
@@ -4,7 +4,9 @@
#include "components/autofill/content/renderer/password_form_conversion_utils.h"
+#include "components/autofill/content/renderer/form_autofill_util.h"
#include "components/autofill/core/common/password_form.h"
+#include "third_party/WebKit/public/web/WebFormControlElement.h"
#include "third_party/WebKit/public/web/WebPasswordFormData.h"
using WebKit::WebFormElement;
@@ -14,6 +16,7 @@ namespace autofill {
namespace {
scoped_ptr<PasswordForm> InitPasswordFormFromWebPasswordForm(
+ const WebFormElement& web_form,
const WebKit::WebPasswordFormData& web_password_form) {
PasswordForm* password_form = new PasswordForm();
password_form->signon_realm = web_password_form.signonRealm.utf8();
@@ -38,6 +41,12 @@ scoped_ptr<PasswordForm> InitPasswordFormFromWebPasswordForm(
password_form->preferred = false;
password_form->blacklisted_by_user = false;
password_form->type = PasswordForm::TYPE_MANUAL;
+ WebFormElementToFormData(web_form,
+ WebKit::WebFormControlElement(),
+ REQUIRE_NONE,
+ EXTRACT_NONE,
+ &password_form->form_data,
+ NULL /* FormFieldData */);
return scoped_ptr<PasswordForm>(password_form);
}
@@ -46,7 +55,7 @@ scoped_ptr<PasswordForm> InitPasswordFormFromWebPasswordForm(
scoped_ptr<PasswordForm> CreatePasswordForm(const WebFormElement& webform) {
WebPasswordFormData web_password_form(webform);
if (web_password_form.isValid())
- return InitPasswordFormFromWebPasswordForm(web_password_form);
+ return InitPasswordFormFromWebPasswordForm(webform, web_password_form);
return scoped_ptr<PasswordForm>();
}
diff --git a/components/autofill/core/browser/autofill_manager.cc b/components/autofill/core/browser/autofill_manager.cc
index 0e2ee3a..6f99772 100644
--- a/components/autofill/core/browser/autofill_manager.cc
+++ b/components/autofill/core/browser/autofill_manager.cc
@@ -253,23 +253,16 @@ bool AutofillManager::OnFormSubmitted(const FormData& form,
// Let Autocomplete know as well.
autocomplete_history_manager_->OnFormSubmitted(form);
- if (!IsAutofillEnabled())
- return false;
+ // Grab a copy of the form data.
+ scoped_ptr<FormStructure> submitted_form(new FormStructure(form));
- if (driver_->GetWebContents()->GetBrowserContext()->IsOffTheRecord())
+ if (!ShouldUploadForm(*submitted_form))
return false;
// Don't save data that was submitted through JavaScript.
if (!form.user_submitted)
return false;
- // Grab a copy of the form data.
- scoped_ptr<FormStructure> submitted_form(new FormStructure(form));
-
- // Disregard forms that we wouldn't ever autofill in the first place.
- if (!submitted_form->ShouldBeParsed(true))
- return false;
-
// Ignore forms not present in our cache. These are typically forms with
// wonky JavaScript that also makes them not auto-fillable.
FormStructure* cached_submitted_form;
@@ -779,6 +772,51 @@ void AutofillManager::UploadFormData(const FormStructure& submitted_form) {
non_empty_types);
}
+bool AutofillManager::UploadPasswordGenerationForm(const FormData& form) {
+ FormStructure form_structure(form);
+
+ if (!ShouldUploadForm(form_structure))
+ return false;
+
+ if (!form_structure.ShouldBeCrowdsourced())
+ return false;
+
+ // TODO(gcasto): Check that PasswordGeneration is enabled?
+
+ // Find the first password field to label. We don't try to label anything
+ // else.
+ bool found_password_field = false;
+ for (size_t i = 0; i < form_structure.field_count(); ++i) {
+ AutofillField* field = form_structure.field(i);
+
+ ServerFieldTypeSet types;
+ if (!found_password_field && field->form_control_type == "password") {
+ types.insert(ACCOUNT_CREATION_PASSWORD);
+ found_password_field = true;
+ } else {
+ types.insert(UNKNOWN_TYPE);
+ }
+ field->set_possible_types(types);
+ }
+ DCHECK(found_password_field);
+
+ // Only one field type should be present.
+ ServerFieldTypeSet available_field_types;
+ available_field_types.insert(ACCOUNT_CREATION_PASSWORD);
+
+ // Force uploading as these events are relatively rare and we want to make
+ // sure to receive them. It also makes testing easier if these requests
+ // always pass.
+ form_structure.set_upload_required(UPLOAD_REQUIRED);
+
+ if (!download_manager_)
+ return false;
+
+ return download_manager_->StartUploadRequest(form_structure,
+ false /* was_autofilled */,
+ available_field_types);
+}
+
void AutofillManager::Reset() {
form_structures_.clear();
has_logged_autofill_enabled_ = false;
@@ -1132,4 +1170,18 @@ void AutofillManager::UpdateInitialInteractionTimestamp(
}
}
+bool AutofillManager::ShouldUploadForm(const FormStructure& form) {
+ if (!IsAutofillEnabled())
+ return false;
+
+ if (driver_->GetWebContents()->GetBrowserContext()->IsOffTheRecord())
+ return false;
+
+ // Disregard forms that we wouldn't ever autofill in the first place.
+ if (!form.ShouldBeParsed(true))
+ return false;
+
+ return true;
+}
+
} // namespace autofill
diff --git a/components/autofill/core/browser/autofill_manager.h b/components/autofill/core/browser/autofill_manager.h
index ec55abd3..a149e28 100644
--- a/components/autofill/core/browser/autofill_manager.h
+++ b/components/autofill/core/browser/autofill_manager.h
@@ -157,6 +157,16 @@ class AutofillManager : public AutofillDownloadManager::Observer {
void OnRequestAutocomplete(const FormData& form,
const GURL& frame_url);
+ // Try and upload |form|. This differs from OnFormSubmitted() in a few ways.
+ // - This function will only label the first <input type="password"> field
+ // as ACCOUNT_CREATION_PASSWORD. Other fields will stay unlabeled, as they
+ // should have been labeled during the upload for OnFormSubmitted().
+ // - This function does not assume that |form| is being uploaded during
+ // the same browsing session as it was originally submitted (as we may
+ // not have the necessary information to classify the form at that time)
+ // so it bypasses the cache and doesn't log the same quality UMA metrics.
+ bool UploadPasswordGenerationForm(const FormData& form);
+
// Resets cache.
virtual void Reset();
@@ -287,6 +297,9 @@ class AutofillManager : public AutofillDownloadManager::Observer {
void UpdateInitialInteractionTimestamp(
const base::TimeTicks& interaction_timestamp);
+ // Shared code to determine if |form| should be uploaded.
+ bool ShouldUploadForm(const FormStructure& form);
+
// Provides driver-level context to the shared code of the component. Must
// outlive this object.
AutofillDriver* driver_;
diff --git a/components/autofill/core/browser/form_structure.h b/components/autofill/core/browser/form_structure.h
index 4ec8f9c..6382c20 100644
--- a/components/autofill/core/browser/form_structure.h
+++ b/components/autofill/core/browser/form_structure.h
@@ -153,6 +153,9 @@ class FormStructure {
const GURL& source_url() const { return source_url_; }
+ void set_upload_required(UploadRequired required) {
+ upload_required_ = required;
+ }
UploadRequired upload_required() const { return upload_required_; }
virtual std::string server_experiment_id() const;
diff --git a/components/autofill/core/common/autofill_messages.h b/components/autofill/core/common/autofill_messages.h
index 136329e..006744f 100644
--- a/components/autofill/core/common/autofill_messages.h
+++ b/components/autofill/core/common/autofill_messages.h
@@ -63,15 +63,6 @@ IPC_STRUCT_TRAITS_BEGIN(autofill::FormFieldDataPredictions)
IPC_STRUCT_TRAITS_MEMBER(overall_type)
IPC_STRUCT_TRAITS_END()
-IPC_STRUCT_TRAITS_BEGIN(autofill::FormData)
- IPC_STRUCT_TRAITS_MEMBER(name)
- IPC_STRUCT_TRAITS_MEMBER(method)
- IPC_STRUCT_TRAITS_MEMBER(origin)
- IPC_STRUCT_TRAITS_MEMBER(action)
- IPC_STRUCT_TRAITS_MEMBER(user_submitted)
- IPC_STRUCT_TRAITS_MEMBER(fields)
-IPC_STRUCT_TRAITS_END()
-
IPC_STRUCT_TRAITS_BEGIN(autofill::FormDataPredictions)
IPC_STRUCT_TRAITS_MEMBER(data)
IPC_STRUCT_TRAITS_MEMBER(signature)
diff --git a/components/autofill/core/common/autofill_param_traits_macros.h b/components/autofill/core/common/autofill_param_traits_macros.h
index b0a1ccf..91d357a 100644
--- a/components/autofill/core/common/autofill_param_traits_macros.h
+++ b/components/autofill/core/common/autofill_param_traits_macros.h
@@ -13,6 +13,15 @@
IPC_ENUM_TRAITS(autofill::PasswordForm::Type)
+IPC_STRUCT_TRAITS_BEGIN(autofill::FormData)
+ IPC_STRUCT_TRAITS_MEMBER(name)
+ IPC_STRUCT_TRAITS_MEMBER(method)
+ IPC_STRUCT_TRAITS_MEMBER(origin)
+ IPC_STRUCT_TRAITS_MEMBER(action)
+ IPC_STRUCT_TRAITS_MEMBER(user_submitted)
+ IPC_STRUCT_TRAITS_MEMBER(fields)
+IPC_STRUCT_TRAITS_END()
+
IPC_STRUCT_TRAITS_BEGIN(autofill::PasswordForm)
IPC_STRUCT_TRAITS_MEMBER(signon_realm)
IPC_STRUCT_TRAITS_MEMBER(origin)
@@ -31,6 +40,7 @@ IPC_STRUCT_TRAITS_BEGIN(autofill::PasswordForm)
IPC_STRUCT_TRAITS_MEMBER(blacklisted_by_user)
IPC_STRUCT_TRAITS_MEMBER(type)
IPC_STRUCT_TRAITS_MEMBER(times_used)
+ IPC_STRUCT_TRAITS_MEMBER(form_data)
IPC_STRUCT_TRAITS_END()
#endif // COMPONENTS_AUTOFILL_CORE_COMMON_AUTOFILL_PARAM_TRAITS_MACROS_H_
diff --git a/components/autofill/core/common/form_data.cc b/components/autofill/core/common/form_data.cc
index 078a58d..050e2e5 100644
--- a/components/autofill/core/common/form_data.cc
+++ b/components/autofill/core/common/form_data.cc
@@ -6,6 +6,7 @@
#include "base/pickle.h"
#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
#include "components/autofill/core/common/form_field_data.h"
namespace autofill {
@@ -78,6 +79,19 @@ bool FormData::operator!=(const FormData& form) const {
return !operator==(form);
}
+std::ostream& operator<<(std::ostream& os, const FormData& form) {
+ os << UTF16ToUTF8(form.name) << " "
+ << UTF16ToUTF8(form.method) << " "
+ << form.origin << " "
+ << form.action << " "
+ << form.user_submitted << " "
+ << "Fields:";
+ for (size_t i = 0; i < form.fields.size(); ++i) {
+ os << form.fields[i] << ",";
+ }
+ return os;
+}
+
void SerializeFormData(const FormData& form_data, Pickle* pickle) {
pickle->WriteInt(kPickleVersion);
pickle->WriteString16(form_data.name);
diff --git a/components/autofill/core/common/form_data.h b/components/autofill/core/common/form_data.h
index 1d6dffa..b41619c 100644
--- a/components/autofill/core/common/form_data.h
+++ b/components/autofill/core/common/form_data.h
@@ -19,7 +19,7 @@ struct FormData {
FormData(const FormData& data);
~FormData();
- // Used by FormStructureTest.
+ // Used in testing.
bool operator==(const FormData& form) const;
bool operator!=(const FormData& form) const;
@@ -37,6 +37,9 @@ struct FormData {
std::vector<FormFieldData> fields;
};
+// For testing.
+std::ostream& operator<<(std::ostream& os, const FormData& form);
+
// Serialize FormData. Used by the PasswordManager to persist FormData
// pertaining to password forms. Serialized data is appended to |pickle|
void SerializeFormData(const FormData& form_data, Pickle* pickle);
diff --git a/components/autofill/core/common/password_form.cc b/components/autofill/core/common/password_form.cc
index 1f9c762..7bd8fe8 100644
--- a/components/autofill/core/common/password_form.cc
+++ b/components/autofill/core/common/password_form.cc
@@ -2,6 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <iostream>
+
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
#include "components/autofill/core/common/password_form.h"
namespace autofill {
@@ -23,4 +27,53 @@ bool PasswordForm::IsPublicSuffixMatch() const {
return !original_signon_realm.empty();
}
+bool PasswordForm::operator==(const PasswordForm& form) const {
+ return signon_realm == form.signon_realm &&
+ origin == form.origin &&
+ action == form.action &&
+ submit_element == form.submit_element &&
+ username_element == form.username_element &&
+ username_value == form.username_value &&
+ other_possible_usernames == form.other_possible_usernames &&
+ password_element == form.password_element &&
+ password_value == form.password_value &&
+ password_autocomplete_set == form.password_autocomplete_set &&
+ old_password_element == form.old_password_element &&
+ old_password_value == form.old_password_value &&
+ ssl_valid == form.ssl_valid &&
+ preferred == form.preferred &&
+ date_created == form.date_created &&
+ blacklisted_by_user == form.blacklisted_by_user &&
+ type == form.type &&
+ times_used == form.times_used &&
+ form_data == form.form_data;
+}
+
+bool PasswordForm::operator!=(const PasswordForm& form) const {
+ return !operator==(form);
+}
+
+std::ostream& operator<<(std::ostream& os, const PasswordForm& form) {
+ return os << "scheme: " << form.scheme
+ << " signon_realm: " << form.signon_realm
+ << " origin: " << form.origin
+ << " action: " << form.action
+ << " submit_element: " << UTF16ToUTF8(form.submit_element)
+ << " username_elem: " << UTF16ToUTF8(form.username_element)
+ << " username_value: " << UTF16ToUTF8(form.username_value)
+ << " password_elem: " << UTF16ToUTF8(form.password_element)
+ << " password_value: " << UTF16ToUTF8(form.password_value)
+ << " old_password_element: "
+ << UTF16ToUTF8(form.old_password_element)
+ << " old_password_value: " << UTF16ToUTF8(form.old_password_value)
+ << " autocomplete_set:" << form.password_autocomplete_set
+ << " blacklisted: " << form.blacklisted_by_user
+ << " preferred: " << form.preferred
+ << " ssl_valid: " << form.ssl_valid
+ << " date_created: " << form.date_created.ToDoubleT()
+ << " type: " << form.type
+ << " times_used: " << form.times_used
+ << " form_data: " << form.form_data;
+}
+
} // namespace autofill
diff --git a/components/autofill/core/common/password_form.h b/components/autofill/core/common/password_form.h
index 9ecb214..8485225 100644
--- a/components/autofill/core/common/password_form.h
+++ b/components/autofill/core/common/password_form.h
@@ -10,6 +10,7 @@
#include <vector>
#include "base/time/time.h"
+#include "components/autofill/core/common/form_data.h"
#include "url/gurl.h"
namespace autofill {
@@ -180,9 +181,20 @@ struct PasswordForm {
// When parsing an HTML form, this is not used.
int times_used;
+ // Autofill representation of this form. Used to communicate with the
+ // Autofill servers if necessary. Currently this is only used to help
+ // determine forms where we can trigger password generation.
+ //
+ // When parsing an HTML form, this is normally set.
+ FormData form_data;
+
// Returns true if this match was found using public suffix matching.
bool IsPublicSuffixMatch() const;
+ // Equality operators for testing.
+ bool operator==(const PasswordForm& form) const;
+ bool operator!=(const PasswordForm& form) const;
+
PasswordForm();
~PasswordForm();
};
@@ -190,6 +202,10 @@ struct PasswordForm {
// Map username to PasswordForm* for convenience. See password_form_manager.h.
typedef std::map<string16, PasswordForm*> PasswordFormMap;
+// For testing.
+std::ostream& operator<<(std::ostream& os,
+ const autofill::PasswordForm& form);
+
} // namespace autofill
#endif // COMPONENTS_AUTOFILL_CORE_COMMON_PASSWORD_FORM_H__
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index c70d8f6..d8a3894 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -10554,6 +10554,16 @@ other types of suffix sets.
</summary>
</histogram>
+<histogram name="PasswordGeneration.UploadStarted" enum="Boolean">
+ <summary>
+ The number of times that we try to upload a form that we believe should
+ trigger password generation. False means that something about the form would
+ not allow us to try upload (not an Autofillable field, uploading disabled,
+ Autofill servers in backoff, etc.). True does not mean that the upload
+ actually completed successfully, just that it was started.
+ </summary>
+</histogram>
+
<histogram name="PasswordManager.BlacklistedSites">
<summary>
The total number of sites that the user has blacklisted. Recorded by