summaryrefslogtreecommitdiff
path: root/chromium/components/autofill/content/renderer
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-10-12 14:27:29 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-10-13 09:35:20 +0000
commitc30a6232df03e1efbd9f3b226777b07e087a1122 (patch)
treee992f45784689f373bcc38d1b79a239ebe17ee23 /chromium/components/autofill/content/renderer
parent7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3 (diff)
downloadqtwebengine-chromium-85-based.tar.gz
BASELINE: Update Chromium to 85.0.4183.14085-based
Change-Id: Iaa42f4680837c57725b1344f108c0196741f6057 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/components/autofill/content/renderer')
-rw-r--r--chromium/components/autofill/content/renderer/BUILD.gn1
-rw-r--r--chromium/components/autofill/content/renderer/autofill_agent.cc131
-rw-r--r--chromium/components/autofill/content/renderer/autofill_agent.h3
-rw-r--r--chromium/components/autofill/content/renderer/focus_test_utils.h4
-rw-r--r--chromium/components/autofill/content/renderer/form_autofill_util.cc90
-rw-r--r--chromium/components/autofill/content/renderer/form_autofill_util.h32
-rw-r--r--chromium/components/autofill/content/renderer/form_autofill_util_browsertest.cc160
-rw-r--r--chromium/components/autofill/content/renderer/form_cache.cc44
-rw-r--r--chromium/components/autofill/content/renderer/form_tracker.cc11
-rw-r--r--chromium/components/autofill/content/renderer/form_tracker.h4
-rw-r--r--chromium/components/autofill/content/renderer/html_based_username_detector.cc11
-rw-r--r--chromium/components/autofill/content/renderer/html_based_username_detector_browsertest.cc345
-rw-r--r--chromium/components/autofill/content/renderer/password_autofill_agent.cc106
-rw-r--r--chromium/components/autofill/content/renderer/password_autofill_agent.h34
-rw-r--r--chromium/components/autofill/content/renderer/password_form_conversion_utils.cc16
-rw-r--r--chromium/components/autofill/content/renderer/password_form_conversion_utils.h7
-rw-r--r--chromium/components/autofill/content/renderer/password_generation_agent.cc3
-rw-r--r--chromium/components/autofill/content/renderer/password_generation_agent.h3
18 files changed, 780 insertions, 225 deletions
diff --git a/chromium/components/autofill/content/renderer/BUILD.gn b/chromium/components/autofill/content/renderer/BUILD.gn
index 82ce0f34d5a..6ee2bee980b 100644
--- a/chromium/components/autofill/content/renderer/BUILD.gn
+++ b/chromium/components/autofill/content/renderer/BUILD.gn
@@ -80,6 +80,7 @@ jumbo_static_library("test_support") {
"//components/autofill/content/renderer",
"//services/service_manager/public/cpp",
"//skia",
+ "//testing/gmock",
"//testing/gtest",
"//third_party/blink/public:blink",
]
diff --git a/chromium/components/autofill/content/renderer/autofill_agent.cc b/chromium/components/autofill/content/renderer/autofill_agent.cc
index 307fa136447..39e65c63370 100644
--- a/chromium/components/autofill/content/renderer/autofill_agent.cc
+++ b/chromium/components/autofill/content/renderer/autofill_agent.cc
@@ -86,6 +86,7 @@ using blink::WebVector;
namespace autofill {
+using form_util::ExtractMask;
using form_util::FindFormAndFieldForFormControlElement;
using form_util::UnownedCheckoutFormElementsAndFieldSetsToFormData;
using mojom::SubmissionSource;
@@ -98,32 +99,12 @@ namespace {
// upon, instead of multiple in close succession (debounce time).
size_t kWaitTimeForSelectOptionsChangesMs = 50;
-// Gets all the data list values (with corresponding label) for the given
-// element.
-void GetDataListSuggestions(const WebInputElement& element,
- std::vector<base::string16>* values,
- std::vector<base::string16>* labels) {
- for (const auto& option : element.FilteredDataListOptions()) {
- values->push_back(option.Value().Utf16());
- if (option.Value() != option.Label())
- labels->push_back(option.Label().Utf16());
- else
- labels->push_back(base::string16());
- }
-}
-
-// Trim the vector before sending it to the browser process to ensure we
-// don't send too much data through the IPC.
-void TrimStringVectorForIPC(std::vector<base::string16>* strings) {
- // Limit the size of the vector.
- if (strings->size() > kMaxListSize)
- strings->resize(kMaxListSize);
-
- // Limit the size of the strings in the vector.
- for (size_t i = 0; i < strings->size(); ++i) {
- if ((*strings)[i].length() > kMaxDataLength)
- (*strings)[i].resize(kMaxDataLength);
- }
+// Helper function to return EXTRACT_DATALIST if kAutofillExtractAllDatalist is
+// enabled, otherwise EXTRACT_NONE is returned.
+ExtractMask GetExtractDatalistMask() {
+ return base::FeatureList::IsEnabled(features::kAutofillExtractAllDatalists)
+ ? form_util::EXTRACT_DATALIST
+ : form_util::EXTRACT_NONE;
}
} // namespace
@@ -160,6 +141,9 @@ AutofillAgent::AutofillAgent(content::RenderFrame* render_frame,
&AutofillAgent::BindPendingReceiver, base::Unretained(this)));
}
+// The destructor is not guaranteed to be called. Destruction happens (only)
+// through the OnDestruct() event, which posts a task to delete this object.
+// The process may be killed before this deletion can happen.
AutofillAgent::~AutofillAgent() {
RemoveFormObserver(this);
}
@@ -175,17 +159,13 @@ bool AutofillAgent::FormDataCompare::operator()(const FormData& lhs,
std::tie(rhs.name, rhs.url, rhs.action, rhs.is_form_tag);
}
-void AutofillAgent::DidCommitProvisionalLoad(bool is_same_document_navigation,
- ui::PageTransition transition) {
+void AutofillAgent::DidCommitProvisionalLoad(ui::PageTransition transition) {
blink::WebFrame* frame = render_frame()->GetWebFrame();
// TODO(dvadym): check if we need to check if it is main frame navigation
// http://crbug.com/443155
if (frame->Parent())
return; // Not a top-level navigation.
- if (is_same_document_navigation)
- return;
-
// Navigation to a new page or a page refresh.
element_.Reset();
@@ -225,9 +205,11 @@ void AutofillAgent::DidChangeScrollOffsetImpl(
FormData form;
FormFieldData field;
- if (FindFormAndFieldForFormControlElement(element_, field_data_manager_.get(),
- form_util::EXTRACT_BOUNDS, &form,
- &field)) {
+ if (FindFormAndFieldForFormControlElement(
+ element_, field_data_manager_.get(),
+ static_cast<ExtractMask>(form_util::EXTRACT_BOUNDS |
+ GetExtractDatalistMask()),
+ &form, &field)) {
GetAutofillDriver()->TextFieldDidScroll(form, field, field.bounds);
}
@@ -237,14 +219,6 @@ void AutofillAgent::DidChangeScrollOffsetImpl(
void AutofillAgent::FocusedElementChanged(const WebElement& element) {
was_focused_before_now_ = false;
-
- if ((IsKeyboardAccessoryEnabled() || !focus_requires_scroll_) &&
- !element.IsNull() &&
- element.GetDocument().GetFrame()->HasTransientUserActivation()) {
- focused_node_was_last_clicked_ = true;
- HandleFocusChangeComplete();
- }
-
HidePopup();
if (element.IsNull()) {
@@ -258,14 +232,30 @@ void AutofillAgent::FocusedElementChanged(const WebElement& element) {
const WebInputElement* input = ToWebInputElement(&element);
+ bool focus_moved_to_new_form = false;
if (!last_interacted_form_.IsNull() &&
(!input || last_interacted_form_ != input->Form())) {
// The focused element is not part of the last interacted form (could be
// in a different form).
GetAutofillDriver()->FocusNoLongerOnForm();
- return;
+ focus_moved_to_new_form = true;
+ }
+
+ // Calls HandleFocusChangeComplete() after notifying the focus is no longer on
+ // the previous form, then early return. No need to notify the newly focused
+ // element because that will be done by HandleFocusChangeComplete() which
+ // triggers FormControlElementClicked().
+ // Refer to http://crbug.com/1105254
+ if ((IsKeyboardAccessoryEnabled() || !focus_requires_scroll_) &&
+ !element.IsNull() &&
+ element.GetDocument().GetFrame()->HasTransientUserActivation()) {
+ focused_node_was_last_clicked_ = true;
+ HandleFocusChangeComplete();
}
+ if (focus_moved_to_new_form)
+ return;
+
if (!input || !input->IsEnabled() || input->IsReadOnly() ||
!input->IsTextField())
return;
@@ -274,9 +264,11 @@ void AutofillAgent::FocusedElementChanged(const WebElement& element) {
FormData form;
FormFieldData field;
- if (FindFormAndFieldForFormControlElement(element_, field_data_manager_.get(),
- form_util::EXTRACT_BOUNDS, &form,
- &field)) {
+ if (FindFormAndFieldForFormControlElement(
+ element_, field_data_manager_.get(),
+ static_cast<ExtractMask>(form_util::EXTRACT_BOUNDS |
+ GetExtractDatalistMask()),
+ &form, &field)) {
GetAutofillDriver()->FocusOnFormField(form, field, field.bounds);
}
}
@@ -355,9 +347,11 @@ void AutofillAgent::OnTextFieldDidChange(const WebInputElement& element) {
FormData form;
FormFieldData field;
- if (FindFormAndFieldForFormControlElement(element, field_data_manager_.get(),
- form_util::EXTRACT_BOUNDS, &form,
- &field)) {
+ if (FindFormAndFieldForFormControlElement(
+ element, field_data_manager_.get(),
+ static_cast<ExtractMask>(form_util::EXTRACT_BOUNDS |
+ GetExtractDatalistMask()),
+ &form, &field)) {
GetAutofillDriver()->TextFieldDidChange(form, field, field.bounds,
AutofillTickClock::NowTicks());
}
@@ -616,9 +610,8 @@ bool AutofillAgent::CollectFormlessElements(FormData* output) {
if (control_elements.size() > kMaxParseableFields)
return false;
- const form_util::ExtractMask extract_mask =
- static_cast<form_util::ExtractMask>(form_util::EXTRACT_VALUE |
- form_util::EXTRACT_OPTIONS);
+ const ExtractMask extract_mask = static_cast<ExtractMask>(
+ form_util::EXTRACT_VALUE | form_util::EXTRACT_OPTIONS);
return UnownedCheckoutFormElementsAndFieldSetsToFormData(
fieldsets, control_elements, nullptr, document, field_data_manager_.get(),
@@ -784,15 +777,18 @@ void AutofillAgent::QueryAutofillSuggestions(
FormData form;
FormFieldData field;
- if (!FindFormAndFieldForFormControlElement(element, field_data_manager_.get(),
- form_util::EXTRACT_BOUNDS, &form,
- &field)) {
+ if (!FindFormAndFieldForFormControlElement(
+ element, field_data_manager_.get(),
+ static_cast<ExtractMask>(form_util::EXTRACT_BOUNDS |
+ GetExtractDatalistMask()),
+ &form, &field)) {
// If we didn't find the cached form, at least let autocomplete have a shot
// at providing suggestions.
WebFormControlElementToFormField(
element, nullptr,
- static_cast<form_util::ExtractMask>(form_util::EXTRACT_VALUE |
- form_util::EXTRACT_BOUNDS),
+ static_cast<ExtractMask>(form_util::EXTRACT_VALUE |
+ form_util::EXTRACT_BOUNDS |
+ GetExtractDatalistMask()),
&field);
}
@@ -803,20 +799,15 @@ void AutofillAgent::QueryAutofillSuggestions(
return;
}
- std::vector<base::string16> data_list_values;
- std::vector<base::string16> data_list_labels;
- const WebInputElement* input_element = ToWebInputElement(&element);
- if (input_element) {
- // Find the datalist values and send them to the browser process.
- GetDataListSuggestions(*input_element, &data_list_values,
- &data_list_labels);
- TrimStringVectorForIPC(&data_list_values);
- TrimStringVectorForIPC(&data_list_labels);
+ if (!base::FeatureList::IsEnabled(features::kAutofillExtractAllDatalists)) {
+ if (const WebInputElement* input_element = ToWebInputElement(&element)) {
+ // Find the datalist values and send them to the browser process.
+ form_util::GetDataListSuggestions(*input_element, &field.datalist_values,
+ &field.datalist_labels);
+ }
}
is_popup_possibly_visible_ = true;
-
- GetAutofillDriver()->SetDataList(data_list_values, data_list_labels);
GetAutofillDriver()->QueryFormFieldAutofill(autofill_query_id_, form, field,
field.bounds,
autoselect_first_suggestion);
@@ -1041,7 +1032,9 @@ void AutofillAgent::OnProvisionallySaveForm(
FormData form;
FormFieldData field;
if (FindFormAndFieldForFormControlElement(
- element, field_data_manager_.get(), form_util::EXTRACT_BOUNDS,
+ element, field_data_manager_.get(),
+ static_cast<ExtractMask>(form_util::EXTRACT_BOUNDS |
+ GetExtractDatalistMask()),
&form, &field)) {
GetAutofillDriver()->SelectControlDidChange(form, field, field.bounds);
}
diff --git a/chromium/components/autofill/content/renderer/autofill_agent.h b/chromium/components/autofill/content/renderer/autofill_agent.h
index b0e7943afb2..be2b158814f 100644
--- a/chromium/components/autofill/content/renderer/autofill_agent.h
+++ b/chromium/components/autofill/content/renderer/autofill_agent.h
@@ -168,8 +168,7 @@ class AutofillAgent : public content::RenderFrameObserver,
};
// content::RenderFrameObserver:
- void DidCommitProvisionalLoad(bool is_same_document_navigation,
- ui::PageTransition transition) override;
+ void DidCommitProvisionalLoad(ui::PageTransition transition) override;
void DidFinishDocumentLoad() override;
void DidChangeScrollOffset() override;
void FocusedElementChanged(const blink::WebElement& element) override;
diff --git a/chromium/components/autofill/content/renderer/focus_test_utils.h b/chromium/components/autofill/content/renderer/focus_test_utils.h
index a20563153b8..038e8fcedb0 100644
--- a/chromium/components/autofill/content/renderer/focus_test_utils.h
+++ b/chromium/components/autofill/content/renderer/focus_test_utils.h
@@ -5,9 +5,9 @@
#ifndef COMPONENTS_AUTOFILL_CONTENT_RENDERER_FOCUS_TEST_UTILS_H_
#define COMPONENTS_AUTOFILL_CONTENT_RENDERER_FOCUS_TEST_UTILS_H_
-#include "base/callback.h"
-#include "string"
+#include <string>
+#include "base/callback.h"
#include "third_party/blink/public/web/web_document.h"
namespace autofill {
diff --git a/chromium/components/autofill/content/renderer/form_autofill_util.cc b/chromium/components/autofill/content/renderer/form_autofill_util.cc
index c1f47cb9575..35f247f215c 100644
--- a/chromium/components/autofill/content/renderer/form_autofill_util.cc
+++ b/chromium/components/autofill/content/renderer/form_autofill_util.cc
@@ -1470,11 +1470,6 @@ bool UnownedFormElementsAndFieldSetsToFormData(
FormData* form,
FormFieldData* field) {
form->url = GetCanonicalOriginForDocument(document);
- if (IsAutofillFieldMetadataEnabled() && !document.Body().IsNull()) {
- SCOPED_UMA_HISTOGRAM_TIMER(
- "PasswordManager.ButtonTitlePerformance.NoFormTag");
- form->button_titles = InferButtonTitlesForForm(document.Body());
- }
if (document.GetFrame() && document.GetFrame()->Top()) {
form->main_frame_origin = document.GetFrame()->Top()->GetSecurityOrigin();
} else {
@@ -1514,6 +1509,20 @@ bool ScriptModifiedUsernameAcceptable(
return field_data_manager->FindMachedValue(value);
}
+// Trim the vector before sending it to the browser process to ensure we
+// don't send too much data through the IPC.
+void TrimStringVectorForIPC(std::vector<base::string16>* strings) {
+ // Limit the size of the vector.
+ if (strings->size() > kMaxListSize)
+ strings->resize(kMaxListSize);
+
+ // Limit the size of the strings in the vector.
+ for (auto& string : *strings) {
+ if (string.length() > kMaxDataLength)
+ string.resize(kMaxDataLength);
+ }
+}
+
// Helper function that strips any authentication data, as well as query and
// ref portions of URL.
GURL StripAuthAndParams(const GURL& gurl) {
@@ -1527,6 +1536,20 @@ GURL StripAuthAndParams(const GURL& gurl) {
} // namespace
+void GetDataListSuggestions(const WebInputElement& element,
+ std::vector<base::string16>* values,
+ std::vector<base::string16>* labels) {
+ for (const auto& option : element.FilteredDataListOptions()) {
+ values->push_back(option.Value().Utf16());
+ if (option.Value() != option.Label())
+ labels->push_back(option.Label().Utf16());
+ else
+ labels->push_back(base::string16());
+ }
+ TrimStringVectorForIPC(values);
+ TrimStringVectorForIPC(labels);
+}
+
bool ExtractFormData(const WebFormElement& form_element,
const FieldDataManager& field_data_manager,
FormData* data) {
@@ -1583,7 +1606,7 @@ GURL GetCanonicalOriginForDocument(const WebDocument& document) {
return StripAuthAndParams(full_origin);
}
-GURL GetOriginWithoutAuthForDocument(const WebDocument& document) {
+GURL GetDocumentUrlWithoutAuth(const WebDocument& document) {
GURL::Replacements rep;
rep.ClearUsername();
rep.ClearPassword();
@@ -1647,6 +1670,11 @@ base::string16 GetFormIdentifier(const WebFormElement& form) {
return identifier;
}
+FormRendererId GetFormRendererId(const blink::WebFormElement& form) {
+ return form.IsNull() ? FormRendererId()
+ : FormRendererId(form.UniqueRendererFormId());
+}
+
base::i18n::TextDirection GetTextDirectionForElement(
const blink::WebFormControlElement& element) {
// Use 'text-align: left|right' if set or 'direction' otherwise.
@@ -1766,6 +1794,12 @@ void WebFormControlElementToFormField(
}
}
}
+ if (extract_mask & EXTRACT_DATALIST) {
+ if (auto* input = blink::ToWebInputElement(&element)) {
+ GetDataListSuggestions(*input, &field->datalist_values,
+ &field->datalist_labels);
+ }
+ }
if (!(extract_mask & EXTRACT_VALUE))
return;
@@ -1834,11 +1868,6 @@ bool WebFormElementToFormData(
form->action = GetCanonicalActionForForm(form_element);
form->is_action_empty =
form_element.Action().IsNull() || form_element.Action().IsEmpty();
- if (IsAutofillFieldMetadataEnabled()) {
- SCOPED_UMA_HISTOGRAM_TIMER(
- "PasswordManager.ButtonTitlePerformance.HasFormTag");
- form->button_titles = InferButtonTitlesForForm(form_element);
- }
if (frame->Top()) {
form->main_frame_origin = frame->Top()->GetSecurityOrigin();
} else {
@@ -2179,6 +2208,41 @@ base::string16 FindChildText(const WebNode& node) {
return FindChildTextWithIgnoreList(node, std::set<WebNode>());
}
+ButtonTitleList GetButtonTitles(const WebFormElement& web_form,
+ const WebDocument& document,
+ ButtonTitlesCache* button_titles_cache) {
+ DCHECK(button_titles_cache);
+ if (!IsAutofillFieldMetadataEnabled() && web_form.IsNull())
+ return ButtonTitleList();
+
+ // True if the cache has no entry for |web_form|.
+ bool cache_miss = true;
+ // Iterator pointing to the entry for |web_form| if the entry for |web_form|
+ // is found.
+ ButtonTitlesCache::iterator form_position;
+ std::tie(form_position, cache_miss) = button_titles_cache->emplace(
+ GetFormRendererId(web_form), ButtonTitleList());
+ if (!cache_miss)
+ return form_position->second;
+
+ ButtonTitleList button_titles;
+ DCHECK(!web_form.IsNull() || !document.IsNull());
+ if (web_form.IsNull()) {
+ const WebElement& body = document.Body();
+ if (!body.IsNull()) {
+ SCOPED_UMA_HISTOGRAM_TIMER(
+ "PasswordManager.ButtonTitlePerformance.NoFormTag");
+ button_titles = InferButtonTitlesForForm(body);
+ }
+ } else {
+ SCOPED_UMA_HISTOGRAM_TIMER(
+ "PasswordManager.ButtonTitlePerformance.HasFormTag");
+ button_titles = InferButtonTitlesForForm(web_form);
+ }
+ form_position->second = std::move(button_titles);
+ return form_position->second;
+}
+
base::string16 FindChildTextWithIgnoreListForTesting(
const WebNode& node,
const std::set<WebNode>& divs_to_skip) {
@@ -2192,10 +2256,6 @@ bool InferLabelForElementForTesting(const WebFormControlElement& element,
return InferLabelForElement(element, stop_words, label, label_source);
}
-ButtonTitleList InferButtonTitlesForTesting(const WebElement& form_element) {
- return InferButtonTitlesForForm(form_element);
-}
-
WebFormElement FindFormByUniqueRendererId(WebDocument doc,
FormRendererId form_renderer_id) {
for (const auto& form : doc.Forms()) {
diff --git a/chromium/components/autofill/content/renderer/form_autofill_util.h b/chromium/components/autofill/content/renderer/form_autofill_util.h
index 877d8aa285b..1a33e640255 100644
--- a/chromium/components/autofill/content/renderer/form_autofill_util.h
+++ b/chromium/components/autofill/content/renderer/form_autofill_util.h
@@ -10,6 +10,7 @@
#include <set>
#include <vector>
+#include "base/containers/flat_map.h"
#include "base/i18n/rtl.h"
#include "base/macros.h"
#include "base/strings/string16.h"
@@ -45,6 +46,10 @@ class FieldDataManager;
namespace form_util {
+// Mapping from a form element's render id to results of button titles
+// heuristics for a given form element.
+using ButtonTitlesCache = base::flat_map<FormRendererId, ButtonTitleList>;
+
// A bit field mask to extract data from WebFormControlElement.
// Copied to components/autofill/ios/browser/resources/autofill_controller.js.
enum ExtractMask {
@@ -59,8 +64,18 @@ enum ExtractMask {
// WebFormControlElement.
EXTRACT_BOUNDS = 1 << 3, // Extract bounds from WebFormControlElement,
// could trigger layout if needed.
+ EXTRACT_DATALIST = 1 << 4, // Extract datalist from WebFormControlElement,
+ // the total number of options is up to
+ // kMaxListSize and each option has as far as
+ // kMaxDataLength.
};
+// Gets up to kMaxListSize data list values (with corresponding label) for the
+// given element, each value and label have as far as kMaxDataLength.
+void GetDataListSuggestions(const blink::WebInputElement& element,
+ std::vector<base::string16>* values,
+ std::vector<base::string16>* labels);
+
// Extract FormData from the form element and return whether the operation was
// successful.
bool ExtractFormData(const blink::WebFormElement& form_element,
@@ -89,7 +104,7 @@ bool AreFormContentsVisible(const blink::WebFormElement& form);
// strip unnecessary data (e.g. query params and HTTP credentials).
GURL GetCanonicalActionForForm(const blink::WebFormElement& form);
GURL GetCanonicalOriginForDocument(const blink::WebDocument& document);
-GURL GetOriginWithoutAuthForDocument(const blink::WebDocument& document);
+GURL GetDocumentUrlWithoutAuth(const blink::WebDocument& document);
// Returns true if |element| is a month input element.
bool IsMonthInput(const blink::WebInputElement* element);
@@ -123,6 +138,10 @@ bool IsWebElementVisible(const blink::WebElement& element);
// attribute.
base::string16 GetFormIdentifier(const blink::WebFormElement& form);
+// Returns the |unique_renderer_id| of a given |WebFormElement|. If
+// |WebFormElement::IsNull()|, returns a null renderer ID.
+FormRendererId GetFormRendererId(const blink::WebFormElement& form);
+
// Returns text alignment for |element|.
base::i18n::TextDirection GetTextDirectionForElement(
const blink::WebFormControlElement& element);
@@ -273,6 +292,15 @@ void PreviewSuggestion(const base::string16& suggestion,
// Whitespace is trimmed from text accumulated at descendant nodes.
base::string16 FindChildText(const blink::WebNode& node);
+// Returns the button titles for |web_form| (or unowned buttons in |document| if
+// |web_form| is null). |button_titles_cache| can be used to spare recomputation
+// if called multiple times for the same form. Button titles computation for
+// unowned buttons is enabled only in Dev and Canary (crbug.com/1086446),
+// otherwise the method returns an empty list.
+ButtonTitleList GetButtonTitles(const blink::WebFormElement& web_form,
+ const blink::WebDocument& document,
+ ButtonTitlesCache* button_titles_cache);
+
// Exposed for testing purpose
base::string16 FindChildTextWithIgnoreListForTesting(
const blink::WebNode& node,
@@ -281,8 +309,6 @@ bool InferLabelForElementForTesting(const blink::WebFormControlElement& element,
const std::vector<base::char16>& stop_words,
base::string16* label,
FormFieldData::LabelSource* label_source);
-ButtonTitleList InferButtonTitlesForTesting(
- const blink::WebElement& form_element);
// Returns form by unique renderer id. Return null element if there is no form
// with given form renderer id.
diff --git a/chromium/components/autofill/content/renderer/form_autofill_util_browsertest.cc b/chromium/components/autofill/content/renderer/form_autofill_util_browsertest.cc
index 9693e877d2f..34575e77b47 100644
--- a/chromium/components/autofill/content/renderer/form_autofill_util_browsertest.cc
+++ b/chromium/components/autofill/content/renderer/form_autofill_util_browsertest.cc
@@ -4,12 +4,15 @@
#include "components/autofill/content/renderer/form_autofill_util.h"
+#include "base/metrics/field_trial.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
#include "components/autofill/core/common/mojom/autofill_types.mojom-shared.h"
#include "components/autofill/core/common/renderer_id.h"
#include "content/public/test/render_view_test.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/public/platform/web_vector.h"
@@ -138,6 +141,14 @@ const char kDivTableExample6[] =
// TODO(crbug.com/796918): Should be "label" or "label-"
const char kDivTableExample6Expected[] = "";
+void VerifyButtonTitleCache(const WebFormElement& form_target,
+ const ButtonTitleList& expected_button_titles,
+ const ButtonTitlesCache& actual_cache) {
+ EXPECT_THAT(actual_cache,
+ testing::ElementsAre(testing::Pair(GetFormRendererId(form_target),
+ expected_button_titles)));
+}
+
class FormAutofillUtilsTest : public content::RenderViewTest {
public:
FormAutofillUtilsTest() {}
@@ -272,8 +283,8 @@ TEST_F(FormAutofillUtilsTest, InferLabelSourceTest) {
}
}
-TEST_F(FormAutofillUtilsTest, InferButtonTitleForFormTest) {
- const char kHtml[] =
+TEST_F(FormAutofillUtilsTest, GetButtonTitles) {
+ constexpr char kHtml[] =
"<form id='target'>"
" <input type='button' value='Clear field'>"
" <input type='button' value='Clear field'>"
@@ -294,8 +305,11 @@ TEST_F(FormAutofillUtilsTest, InferButtonTitleForFormTest) {
ASSERT_FALSE(target.IsNull());
const WebFormElement& form_target = target.ToConst<WebFormElement>();
ASSERT_FALSE(form_target.IsNull());
+ ButtonTitlesCache cache;
+
+ autofill::ButtonTitleList actual =
+ GetButtonTitles(form_target, web_frame->GetDocument(), &cache);
- autofill::ButtonTitleList actual = InferButtonTitlesForTesting(form_target);
autofill::ButtonTitleList expected = {
{base::UTF8ToUTF16("Clear field"),
ButtonTitleType::INPUT_ELEMENT_BUTTON_TYPE},
@@ -309,9 +323,11 @@ TEST_F(FormAutofillUtilsTest, InferButtonTitleForFormTest) {
{base::UTF8ToUTF16("Join"), ButtonTitleType::DIV},
{base::UTF8ToUTF16("Start"), ButtonTitleType::SPAN}};
EXPECT_EQ(expected, actual);
+
+ VerifyButtonTitleCache(form_target, expected, cache);
}
-TEST_F(FormAutofillUtilsTest, InferButtonTitleForFormTest_TooLongTitle) {
+TEST_F(FormAutofillUtilsTest, GetButtonTitles_TooLongTitle) {
std::string title;
for (int i = 0; i < 300; ++i)
title += "a";
@@ -330,8 +346,10 @@ TEST_F(FormAutofillUtilsTest, InferButtonTitleForFormTest_TooLongTitle) {
ASSERT_FALSE(target.IsNull());
const WebFormElement& form_target = target.ToConst<WebFormElement>();
ASSERT_FALSE(form_target.IsNull());
+ ButtonTitlesCache cache;
- autofill::ButtonTitleList actual = InferButtonTitlesForTesting(form_target);
+ autofill::ButtonTitleList actual =
+ GetButtonTitles(form_target, web_frame->GetDocument(), &cache);
int total_length = 0;
for (auto title : actual) {
@@ -341,8 +359,14 @@ TEST_F(FormAutofillUtilsTest, InferButtonTitleForFormTest_TooLongTitle) {
EXPECT_EQ(200, total_length);
}
-TEST_F(FormAutofillUtilsTest, InferButtonTitle_Formless) {
- const char kNoFormHtml[] =
+TEST_F(FormAutofillUtilsTest, GetButtonTitles_Formless) {
+ // Button titles computation and crowdsourcing for <form>less forms are
+ // enabled only if |AutofillFieldMetadata| (Dev and Canary) is enabled.
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.Init();
+ base::FieldTrialList::CreateFieldTrial("AutofillFieldMetadata", "Enabled");
+
+ constexpr char kNoFormHtml[] =
"<div class='reg-form'>"
" <input type='button' value='\n Show\t password '>"
" <button>Sign Up</button>"
@@ -358,10 +382,12 @@ TEST_F(FormAutofillUtilsTest, InferButtonTitle_Formless) {
LoadHTML(kNoFormHtml);
WebLocalFrame* web_frame = GetMainFrame();
ASSERT_NE(nullptr, web_frame);
- const WebElement& body = web_frame->GetDocument().Body();
- ASSERT_FALSE(body.IsNull());
+ WebFormElement form_target;
+ ASSERT_FALSE(web_frame->GetDocument().Body().IsNull());
+ ButtonTitlesCache cache;
- autofill::ButtonTitleList actual = InferButtonTitlesForTesting(body);
+ autofill::ButtonTitleList actual =
+ GetButtonTitles(form_target, web_frame->GetDocument(), &cache);
autofill::ButtonTitleList expected = {
{base::UTF8ToUTF16("Show password"),
ButtonTitleType::INPUT_ELEMENT_BUTTON_TYPE},
@@ -370,6 +396,42 @@ TEST_F(FormAutofillUtilsTest, InferButtonTitle_Formless) {
{base::UTF8ToUTF16("Register"),
ButtonTitleType::BUTTON_ELEMENT_BUTTON_TYPE}};
EXPECT_EQ(expected, actual);
+
+ VerifyButtonTitleCache(form_target, expected, cache);
+}
+
+TEST_F(FormAutofillUtilsTest, GetButtonTitles_Formless_DisabledByDefault) {
+ // Button titles computation and crowdsourcing for <form>less forms should be
+ // disabled if |AutofillFieldMetadata| is disabled.
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.Init();
+ base::FieldTrialList::CreateFieldTrial("AutofillFieldMetadata", "Disabled");
+
+ constexpr char kNoFormHtml[] =
+ "<div class='reg-form'>"
+ " <input type='button' value='\n Show\t password '>"
+ " <button>Sign Up</button>"
+ " <button type='button'>Register</button>"
+ "</div>"
+ "<form id='ignored-form'>"
+ " <input type='button' value='Ignore this'>"
+ " <button>Ignore this</button>"
+ " <a id='Submit' value='Ignore this'>"
+ " <div name='BTN'>Ignore this</div>"
+ "</form>";
+
+ LoadHTML(kNoFormHtml);
+ WebLocalFrame* web_frame = GetMainFrame();
+ ASSERT_NE(nullptr, web_frame);
+ WebFormElement form_target;
+ ASSERT_FALSE(web_frame->GetDocument().Body().IsNull());
+ ButtonTitlesCache cache;
+
+ autofill::ButtonTitleList actual =
+ GetButtonTitles(form_target, web_frame->GetDocument(), &cache);
+
+ EXPECT_TRUE(actual.empty());
+ EXPECT_TRUE(cache.empty());
}
TEST_F(FormAutofillUtilsTest, IsEnabled) {
@@ -788,6 +850,84 @@ TEST_F(FormAutofillUtilsTest, ExtractUnownedBounds) {
EXPECT_FALSE(form_data.fields.back().bounds.IsEmpty());
}
+TEST_F(FormAutofillUtilsTest, GetDataListSuggestions) {
+ LoadHTML(
+ "<body><input list='datalist_id' name='count' id='i1'><datalist "
+ "id='datalist_id'><option value='1'><option "
+ "value='2'></datalist></body>");
+ WebDocument doc = GetMainFrame()->GetDocument();
+ auto web_control = doc.GetElementById("i1").To<WebInputElement>();
+ std::vector<base::string16> values;
+ std::vector<base::string16> labels;
+ GetDataListSuggestions(web_control, &values, &labels);
+ ASSERT_EQ(values.size(), 2u);
+ ASSERT_EQ(labels.size(), 2u);
+ EXPECT_EQ(values[0], base::UTF8ToUTF16("1"));
+ EXPECT_EQ(values[1], base::UTF8ToUTF16("2"));
+ EXPECT_EQ(labels[0], base::UTF8ToUTF16(""));
+ EXPECT_EQ(labels[1], base::UTF8ToUTF16(""));
+}
+
+TEST_F(FormAutofillUtilsTest, GetDataListSuggestionsWithLabels) {
+ LoadHTML(
+ "<body><input list='datalist_id' name='count' id='i1'><datalist "
+ "id='datalist_id'><option value='1'>one</option><option "
+ "value='2'>two</option></datalist></body>");
+ WebDocument doc = GetMainFrame()->GetDocument();
+ auto web_control = doc.GetElementById("i1").To<WebInputElement>();
+ std::vector<base::string16> values;
+ std::vector<base::string16> labels;
+ GetDataListSuggestions(web_control, &values, &labels);
+ ASSERT_EQ(values.size(), 2u);
+ ASSERT_EQ(labels.size(), 2u);
+ EXPECT_EQ(values[0], base::UTF8ToUTF16("1"));
+ EXPECT_EQ(values[1], base::UTF8ToUTF16("2"));
+ EXPECT_EQ(labels[0], base::UTF8ToUTF16("one"));
+ EXPECT_EQ(labels[1], base::UTF8ToUTF16("two"));
+}
+
+TEST_F(FormAutofillUtilsTest, ExtractDataList) {
+ LoadHTML(
+ "<body><input list='datalist_id' name='count' id='i1'><datalist "
+ "id='datalist_id'><option value='1'>one</option><option "
+ "value='2'>two</option></datalist></body>");
+ WebDocument doc = GetMainFrame()->GetDocument();
+ auto web_control = doc.GetElementById("i1").To<WebInputElement>();
+ FormData form_data;
+ FormFieldData form_field_data;
+ ASSERT_TRUE(FindFormAndFieldForFormControlElement(
+ web_control, nullptr /*field_data_manager*/, EXTRACT_DATALIST, &form_data,
+ &form_field_data));
+
+ auto& values = form_data.fields.back().datalist_values;
+ auto& labels = form_data.fields.back().datalist_labels;
+ ASSERT_EQ(values.size(), 2u);
+ ASSERT_EQ(labels.size(), 2u);
+ EXPECT_EQ(values[0], base::UTF8ToUTF16("1"));
+ EXPECT_EQ(values[1], base::UTF8ToUTF16("2"));
+ EXPECT_EQ(labels[0], base::UTF8ToUTF16("one"));
+ EXPECT_EQ(labels[1], base::UTF8ToUTF16("two"));
+ EXPECT_EQ(form_field_data.datalist_values, values);
+ EXPECT_EQ(form_field_data.datalist_labels, labels);
+}
+
+TEST_F(FormAutofillUtilsTest, NotExtractDataList) {
+ LoadHTML(
+ "<body><input list='datalist_id' name='count' id='i1'><datalist "
+ "id='datalist_id'><option value='1'>one</option><option "
+ "value='2'>two</option></datalist></body>");
+ WebDocument doc = GetMainFrame()->GetDocument();
+ auto web_control = doc.GetElementById("i1").To<WebInputElement>();
+ FormData form_data;
+ FormFieldData form_field_data;
+ ASSERT_TRUE(FindFormAndFieldForFormControlElement(
+ web_control, nullptr /*field_data_manager*/, &form_data,
+ &form_field_data));
+
+ EXPECT_TRUE(form_data.fields.back().datalist_values.empty());
+ EXPECT_TRUE(form_data.fields.back().datalist_labels.empty());
+}
+
} // namespace
} // namespace form_util
} // namespace autofill
diff --git a/chromium/components/autofill/content/renderer/form_cache.cc b/chromium/components/autofill/content/renderer/form_cache.cc
index 1bde0197a15..fe638091f41 100644
--- a/chromium/components/autofill/content/renderer/form_cache.cc
+++ b/chromium/components/autofill/content/renderer/form_cache.cc
@@ -18,6 +18,7 @@
#include "base/strings/string_split.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/strings/string_number_conversions.h"
#include "components/autofill/content/renderer/form_autofill_util.h"
#include "components/autofill/content/renderer/page_form_analyser_logger.h"
#include "components/autofill/core/common/autofill_constants.h"
@@ -420,37 +421,19 @@ bool FormCache::ShowPredictions(const FormDataPredictions& form,
std::vector<WebFormControlElement> control_elements;
- // First check the synthetic form.
- bool found_synthetic_form = false;
- if (form.data.SameFormAs(synthetic_form_)) {
- found_synthetic_form = true;
+ if (form.data.unique_renderer_id.is_null()) { // Form is synthetic.
WebDocument document = frame_->GetDocument();
control_elements = form_util::GetUnownedAutofillableFormFieldElements(
document.All(), nullptr);
- }
-
- if (!found_synthetic_form) {
- // Find the real form by searching through the WebDocuments.
- bool found_form = false;
-
+ } else {
for (const WebFormElement& form_element : frame_->GetDocument().Forms()) {
- // To match two forms, we look for the form's name and the number of
- // fields on that form. (Form names may not be unique.)
- // Note: WebString() == WebString(string16()) does not evaluate to |true|
- // -- WebKit distinguishes between a "null" string (lhs) and an "empty"
- // string (rhs). We don't want that distinction, so forcing to string16.
- base::string16 element_name = form_util::GetFormIdentifier(form_element);
- if (element_name == form.data.name) {
- found_form = true;
+ FormRendererId form_id(form_element.UniqueRendererFormId());
+ if (form_id == form.data.unique_renderer_id) {
control_elements =
form_util::ExtractAutofillableElementsInForm(form_element);
- if (control_elements.size() == form.fields.size())
- break;
+ break;
}
}
-
- if (!found_form)
- return false;
}
if (control_elements.size() != form.fields.size()) {
@@ -464,11 +447,9 @@ bool FormCache::ShowPredictions(const FormDataPredictions& form,
WebFormControlElement& element = control_elements[i];
const FormFieldData& field_data = form.data.fields[i];
- if (element.NameForAutofill().Utf16() != field_data.name) {
- // Keep things simple. Don't show predictions for elements whose names
- // were modified between page load and the server's response to our query.
+ FieldRendererId field_id(element.UniqueRendererFormControlId());
+ if (field_id != field_data.unique_renderer_id)
continue;
- }
const FormFieldDataPredictions& field = form.fields[i];
// Possibly add a console warning for this field regarding the usage of
@@ -492,6 +473,11 @@ bool FormCache::ShowPredictions(const FormDataPredictions& form,
const base::string16 truncated_label = field_data.label.substr(
0, std::min(field_data.label.length(), kMaxLabelSize));
+ std::string form_id =
+ base::NumberToString(form.data.unique_renderer_id.value());
+ std::string field_id =
+ base::NumberToString(field.field.unique_renderer_id.value());
+
std::string title =
base::StrCat({"overall type: ", field.overall_type, //
"\nserver type: ", field.server_type, //
@@ -500,7 +486,9 @@ bool FormCache::ShowPredictions(const FormDataPredictions& form,
"\nparseable name: ", field.parseable_name, //
"\nsection: ", field.section, //
"\nfield signature: ", field.signature, //
- "\nform signature: ", form.signature});
+ "\nform signature: ", form.signature, //
+ "\nform renderer id: ", form_id, //
+ "\nfield renderer id: ", field_id});
// Set this debug string to the title so that a developer can easily debug
// by hovering the mouse over the input field.
diff --git a/chromium/components/autofill/content/renderer/form_tracker.cc b/chromium/components/autofill/content/renderer/form_tracker.cc
index e077f40a8b7..8613e8d3938 100644
--- a/chromium/components/autofill/content/renderer/form_tracker.cc
+++ b/chromium/components/autofill/content/renderer/form_tracker.cc
@@ -126,14 +126,13 @@ void FormTracker::FormControlDidChangeImpl(
}
}
-void FormTracker::DidCommitProvisionalLoad(bool is_same_document_navigation,
- ui::PageTransition transition) {
+void FormTracker::DidCommitProvisionalLoad(ui::PageTransition transition) {
DCHECK_CALLED_ON_VALID_SEQUENCE(form_tracker_sequence_checker_);
- if (!is_same_document_navigation) {
- ResetLastInteractedElements();
- return;
- }
+ ResetLastInteractedElements();
+}
+void FormTracker::DidFinishSameDocumentNavigation() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(form_tracker_sequence_checker_);
FireSubmissionIfFormDisappear(SubmissionSource::SAME_DOCUMENT_NAVIGATION);
}
diff --git a/chromium/components/autofill/content/renderer/form_tracker.h b/chromium/components/autofill/content/renderer/form_tracker.h
index ed4e403ff26..1ac3a8a1272 100644
--- a/chromium/components/autofill/content/renderer/form_tracker.h
+++ b/chromium/components/autofill/content/renderer/form_tracker.h
@@ -87,8 +87,8 @@ class FormTracker : public content::RenderFrameObserver {
FormSubmittedBySameDocumentNavigation);
// content::RenderFrameObserver:
- void DidCommitProvisionalLoad(bool is_same_document_navigation,
- ui::PageTransition transition) override;
+ void DidCommitProvisionalLoad(ui::PageTransition transition) override;
+ void DidFinishSameDocumentNavigation() override;
void DidStartNavigation(
const GURL& url,
base::Optional<blink::WebNavigationType> navigation_type) override;
diff --git a/chromium/components/autofill/content/renderer/html_based_username_detector.cc b/chromium/components/autofill/content/renderer/html_based_username_detector.cc
index 9929ea02fd1..1006ccd24ab 100644
--- a/chromium/components/autofill/content/renderer/html_based_username_detector.cc
+++ b/chromium/components/autofill/content/renderer/html_based_username_detector.cc
@@ -284,13 +284,6 @@ void FindUsernameFieldInternal(
}
}
-// Returns the |unique_renderer_id| of a given |WebFormElement|. If
-// |WebFormElement::IsNull()| return a null renderer ID.
-FormRendererId GetFormRendererId(WebFormElement form) {
- return form.IsNull() ? FormRendererId()
- : FormRendererId(form.UniqueRendererFormId());
-}
-
} // namespace
const std::vector<FieldRendererId>& GetPredictionsFieldBasedOnHtmlAttributes(
@@ -311,8 +304,8 @@ const std::vector<FieldRendererId>& GetPredictionsFieldBasedOnHtmlAttributes(
bool cache_miss = true;
// Iterator pointing to the entry for |form| if the entry for |form| is found.
UsernameDetectorCache::iterator form_position;
- std::tie(form_position, cache_miss) = username_detector_cache->insert(
- std::make_pair(GetFormRendererId(form), std::vector<FieldRendererId>()));
+ std::tie(form_position, cache_miss) = username_detector_cache->emplace(
+ form_util::GetFormRendererId(form), std::vector<FieldRendererId>());
if (cache_miss) {
std::vector<FieldRendererId> username_predictions;
diff --git a/chromium/components/autofill/content/renderer/html_based_username_detector_browsertest.cc b/chromium/components/autofill/content/renderer/html_based_username_detector_browsertest.cc
new file mode 100644
index 00000000000..d39bca5d8cd
--- /dev/null
+++ b/chromium/components/autofill/content/renderer/html_based_username_detector_browsertest.cc
@@ -0,0 +1,345 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/strings/stringprintf.h"
+#include "components/autofill/content/renderer/form_autofill_util.h"
+#include "components/autofill/content/renderer/html_based_username_detector.h"
+#include "content/public/test/render_view_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/web/web_document.h"
+#include "third_party/blink/public/web/web_local_frame.h"
+
+using blink::WebElement;
+using blink::WebFormControlElement;
+using blink::WebFormElement;
+using blink::WebLocalFrame;
+using blink::WebString;
+
+namespace autofill {
+
+namespace {
+
+struct TextField {
+ const char* name;
+ const char* id;
+ const char* value;
+ const char* label;
+};
+
+constexpr char kTestForm[] = R"(
+ <FORM name="Test" id="userform">
+ <label for="%s">%s</label>
+ <input type="text" name="%s" id="%s" value="%s" />
+
+ <label for="%s">%s</label>
+ <input type="text" name="%s" id="%s" value="%s" />
+
+ <input type="password" id="password" value="v" />
+ <input type="submit" value="submit" />
+ </FORM>
+)";
+
+std::string GetFormHTML(const TextField& first_field,
+ const TextField& second_field) {
+ return base::StringPrintf(
+ kTestForm, first_field.id, first_field.label, first_field.name,
+ first_field.id, first_field.value, second_field.id, second_field.label,
+ second_field.name, second_field.id, second_field.value);
+}
+
+class HtmlBasedUsernameDetectorTest : public content::RenderViewTest {
+ protected:
+ struct TestCase {
+ const TextField first_text_field_parameter;
+ const TextField second_text_field_parameter;
+ const WebString expected_username_id;
+ };
+
+ FormData LoadFormDataFromHtml(const std::string& html) {
+ LoadHTML(html.data());
+ const WebFormElement& form = GetFormElement();
+ return GetFormDataFromForm(form);
+ }
+
+ FormData GetFormDataFromForm(const WebFormElement& form) {
+ FormData form_data;
+ EXPECT_TRUE(form_util::WebFormElementToFormData(
+ form, WebFormControlElement(), nullptr, form_util::EXTRACT_NONE,
+ &form_data, nullptr));
+
+ return form_data;
+ }
+
+ FieldRendererId GetRendererIdFromWebElementId(const WebString& id) {
+ const WebLocalFrame* frame = GetMainFrame();
+ const WebElement& element = frame->GetDocument().GetElementById(id);
+ EXPECT_FALSE(element.IsNull());
+ return FieldRendererId(element.ToConst<blink::WebInputElement>()
+ .UniqueRendererFormControlId());
+ }
+
+ WebFormElement GetFormElement() {
+ const WebLocalFrame* frame = GetMainFrame();
+ const blink::WebVector<WebFormElement>& forms =
+ frame->GetDocument().Forms();
+ EXPECT_EQ(1U, forms.size());
+ EXPECT_FALSE(forms[0].IsNull());
+
+ return forms[0];
+ }
+
+ std::vector<WebFormControlElement> GetFormControlElements() {
+ const WebFormElement& form = GetFormElement();
+ blink::WebVector<WebFormControlElement> control_elements =
+ form.GetFormControlElements();
+ return control_elements.ReleaseVector();
+ }
+
+ void PredictAndCheckUsernameId(const std::string& html,
+ const WebString& expected_username_id) {
+ const FormData& form_data = LoadFormDataFromHtml(html);
+ const std::vector<WebFormControlElement>& control_elements =
+ GetFormControlElements();
+
+ // Get the expected renderer id from the expected username id.
+ const FieldRendererId expected_renderer_id =
+ GetRendererIdFromWebElementId(expected_username_id);
+
+ // Run predictions and test the result.
+ UsernameDetectorCache cache;
+ const std::vector<FieldRendererId>& renderer_ids =
+ GetPredictionsFieldBasedOnHtmlAttributes(control_elements, form_data,
+ &cache);
+
+ ASSERT_EQ(1u, cache.size());
+ ASSERT_FALSE(cache.begin()->second.empty());
+ EXPECT_EQ(expected_renderer_id, cache.begin()->second[0]);
+ ASSERT_FALSE(renderer_ids.empty());
+ EXPECT_EQ(expected_renderer_id, renderer_ids[0]);
+ }
+};
+
+} // namespace
+
+TEST_F(HtmlBasedUsernameDetectorTest, DeveloperGroupAttributes) {
+ // Each test case consists of a set of parameters to be plugged into
+ // the TestCase struct, plus the corresponding expectations. The test data
+ // contains cases that are identified by HTML detector, and not by
+ // base heuristic. Thus, username field does not necessarely have to
+ // be right before password field. These tests basically check
+ // searching in developer group (i.e. name and id attribute,
+ // concatenated, with "$" guard in between).
+ const TestCase test_cases[] = {
+ // There are both field name and id.
+ {{"username", "x1d", "johnsmith"},
+ {"email", "y1d", "js@google.com"},
+ "x1d"},
+ // there is no field id.
+ {{"username", "x1d", "johnsmith"},
+ {"email", "y1d", "js@google.com"},
+ "x1d"},
+ // Upper or mixed case shouldn't matter.
+ {{"uSeRnAmE", "x1d", "johnsmith"},
+ {"email", "y1d", "js@google.com"},
+ "x1d"},
+ // Check removal of special characters.
+ {{"u1_s2-e3~r4/n5(a)6m#e", "x1d", "johnsmith"},
+ {"email", "y1d", "js@google.com"},
+ "x1d"},
+ // Check guard between field name and field id.
+ {{"us", "ername", "johnsmith"}, {"email", "id", "js@google.com"}, "id"},
+ // Check removal of fields with latin negative words in developer group.
+ {{"email", "x", "js@google.com"},
+ {"fake_username", "y", "johnsmith"},
+ "x"},
+ {{"email", "mail", "js@google.com"},
+ {"user_name", "fullname", "johnsmith"},
+ "mail"},
+ // Identify latin translations of "username".
+ {{"benutzername", "x", "johnsmith"},
+ {"email", "y", "js@google.com"},
+ "x"},
+ // Identify latin translations of "user".
+ {{"utilizator", "x1d", "johnsmith"},
+ {"email", "y1d", "js@google.com"},
+ "x1d"},
+ // Identify technical words.
+ {{"loginid", "x1d", "johnsmith"},
+ {"email", "y1d", "js@google.com"},
+ "x1d"},
+ // Identify weak words.
+ {{"usrname", "x1d", "johnsmith"},
+ {"email", "y1d", "js@google.com"},
+ "y1d"},
+ // If a word matches in maximum 2 fields, it is accepted.
+ // First encounter is selected as username.
+ {{"username", "x1d", "johnsmith"},
+ {"repeat_username", "y1d", "johnsmith"},
+ "x1d"},
+ // A short word should be enclosed between delimiters. Otherwise, an
+ // Occurrence doesn't count.
+ {{"identity_name", "idn", "johnsmith"}, {"id", "xid", "123"}, "xid"}};
+
+ for (size_t i = 0; i < base::size(test_cases); ++i) {
+ SCOPED_TRACE(testing::Message() << "Iteration " << i);
+
+ const std::string& form_html =
+ GetFormHTML(test_cases[i].first_text_field_parameter,
+ test_cases[i].second_text_field_parameter);
+
+ PredictAndCheckUsernameId(form_html, test_cases[i].expected_username_id);
+ }
+}
+
+TEST_F(HtmlBasedUsernameDetectorTest, UserGroupAttributes) {
+ // Each test case consists of a set of parameters to be plugged into
+ // the TestCase struct, plus the corresponding expectations. The test data
+ // contains cases that are identified by HTML detector, and not by
+ // base heuristic. Thus, username field does not necessarely have to
+ // be right before password field. These tests basically check
+ // searching in user group
+ const TestCase test_cases[] = {
+ // Label information will decide username.
+ {{"name1", "id1", "johnsmith", "Username:"},
+ {"name2", "id2", "js@google.com", "Email:"},
+ "id1"},
+ // Placeholder information will decide username.
+ {{"name1", "id1", "js@google.com", "Email:"},
+ {"name2", "id2", "johnsmith", "Username:"},
+ "id2"},
+ // Check removal of special characters.
+ {{"name1", "id1", "johnsmith", "U s er n a m e:"},
+ {"name2", "id2", "js@google.com", "Email:"},
+ "id1"},
+ // Check removal of fields with latin negative words in user group.
+ {{"name1", "id1", "johnsmith", "Username password:"},
+ {"name2", "id2", "js@google.com", "Email:"},
+ "id2"},
+ // Check removal of fields with non-latin negative words in user group.
+ {{"name1", "id1", "js@google.com", "Email:"},
+ {"name2", "id2", "johnsmith", "የይለፍቃልየይለፍቃል:"},
+ "id1"},
+ // Identify latin translations of "username".
+ {{"name1", "id1", "johnsmith", "Username:"},
+ {"name2", "id2", "js@google.com", "Email:"},
+ "id1"},
+ // Identify non-latin translations of "username".
+ {{"name1", "id1", "johnsmith", "用户名:"},
+ {"name2", "id2", "js@google.com", "Email:"},
+ "id1"},
+ // Identify latin translations of "user".
+ {{"name1", "id1", "johnsmith", "Wosuta:"},
+ {"name2", "id2", "js@google.com", "Email:"},
+ "id1"},
+ // Identify non-latin translations of "user".
+ {{"name1", "id1", "johnsmith", "истифода:"},
+ {"name2", "id2", "js@google.com", "Email:"},
+ "id1"},
+ // Identify weak words.
+ {{"name1", "id1", "johnsmith", "Insert your login details:"},
+ {"name2", "id2", "js@google.com", "Insert your email:"},
+ "id1"},
+ // Check user group priority, compared to developer group.
+ // User group should have higher priority than developer group.
+ {{"email", "id1", "js@google.com", "Username:"},
+ {"username", "id2", "johnsmith", "Email:"},
+ "id1"},
+ // Check treatment for short dictionary words. "uid" has higher priority,
+ // but its occurrence is ignored because it is a part of another word.
+ {
+ {"name1", "noword", "johnsmith", "Insert your id:"},
+ {"name2", "uidentical", "js@google.com", "Insert something:"},
+ "noword",
+ }};
+
+ for (size_t i = 0; i < base::size(test_cases); ++i) {
+ SCOPED_TRACE(testing::Message() << "Iteration " << i);
+
+ const std::string& form_html =
+ GetFormHTML(test_cases[i].first_text_field_parameter,
+ test_cases[i].second_text_field_parameter);
+
+ PredictAndCheckUsernameId(form_html, test_cases[i].expected_username_id);
+ }
+}
+
+TEST_F(HtmlBasedUsernameDetectorTest, SeveralDetections) {
+ // If word matches in more than 2 fields, we don't match on it.
+ // We search for match with another word.
+ const std::string& test_form = R"(
+ <form>
+ <input type="text" name="address" id="xuser" value="addr" />
+ <input type="text" name="loginid" id="yuser" value="johnsmith" />
+ <input type="text" name="tel" id="zuser" value="sometel" />
+ <input type="password" id="password" value="v" />
+ <input type="submit" value="submit" />
+ </form>
+ )";
+ PredictAndCheckUsernameId(test_form, "yuser");
+}
+
+TEST_F(HtmlBasedUsernameDetectorTest, HTMLDetectorCache) {
+ const TextField text_fields[] = {
+ {"unknown", "12345"},
+ {"something", "smith"},
+ };
+
+ const std::string& form_html = GetFormHTML(text_fields[0], text_fields[1]);
+
+ FormData form_data = LoadFormDataFromHtml(form_html);
+ std::vector<WebFormControlElement> control_elements =
+ GetFormControlElements();
+
+ UsernameDetectorCache cache;
+ std::vector<FieldRendererId> field_ids =
+ GetPredictionsFieldBasedOnHtmlAttributes(control_elements, form_data,
+ &cache);
+
+ // No signals from HTML attributes. The classifier found nothing and cached
+ // it.
+ ASSERT_EQ(1u, cache.size());
+ EXPECT_TRUE(field_ids.empty());
+ const WebFormElement& form = GetFormElement();
+ EXPECT_EQ(FormRendererId(form.UniqueRendererFormId()), cache.begin()->first);
+ EXPECT_TRUE(cache.begin()->second.empty());
+
+ // Changing attributes would change the classifier's output. But the output
+ // will be the same because it was cached in |username_detector_cache|.
+ control_elements[0].SetAttribute("name", "id");
+ form_data = GetFormDataFromForm(GetFormElement());
+ field_ids = GetPredictionsFieldBasedOnHtmlAttributes(control_elements,
+ form_data, &cache);
+ ASSERT_EQ(1u, cache.size());
+ EXPECT_TRUE(field_ids.empty());
+ EXPECT_EQ(FormRendererId(form.UniqueRendererFormId()), cache.begin()->first);
+ EXPECT_TRUE(cache.begin()->second.empty());
+
+ // Clear the cache. The classifier will find username field and cache it.
+ cache.clear();
+ ASSERT_EQ(4u, control_elements.size());
+ field_ids = GetPredictionsFieldBasedOnHtmlAttributes(control_elements,
+ form_data, &cache);
+ ASSERT_EQ(1u, cache.size());
+ EXPECT_EQ(1u, field_ids.size());
+ EXPECT_EQ(FormRendererId(form.UniqueRendererFormId()), cache.begin()->first);
+ ASSERT_EQ(1u, cache.begin()->second.size());
+ EXPECT_EQ(FieldRendererId(control_elements[0].UniqueRendererFormControlId()),
+ cache.begin()->second[0]);
+
+ // Change the attributes again ("username" is stronger signal than "id"),
+ // but keep the cache. The classifier's output should be the same.
+ control_elements[1].SetAttribute("name", "username");
+ form_data = GetFormDataFromForm(GetFormElement());
+ field_ids = GetPredictionsFieldBasedOnHtmlAttributes(control_elements,
+ form_data, &cache);
+
+ ASSERT_EQ(1u, cache.size());
+ EXPECT_EQ(1u, field_ids.size());
+ EXPECT_EQ(FormRendererId(form.UniqueRendererFormId()), cache.begin()->first);
+ ASSERT_EQ(1u, cache.begin()->second.size());
+ EXPECT_EQ(FieldRendererId(control_elements[0].UniqueRendererFormControlId()),
+ cache.begin()->second[0]);
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/content/renderer/password_autofill_agent.cc b/chromium/components/autofill/content/renderer/password_autofill_agent.cc
index d85705681a9..4d283f72a0f 100644
--- a/chromium/components/autofill/content/renderer/password_autofill_agent.cc
+++ b/chromium/components/autofill/content/renderer/password_autofill_agent.cc
@@ -24,7 +24,6 @@
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
-#include "components/autofill/content/renderer/form_autofill_util.h"
#include "components/autofill/content/renderer/password_form_conversion_utils.h"
#include "components/autofill/content/renderer/password_generation_agent.h"
#include "components/autofill/content/renderer/prefilled_values_detector.h"
@@ -36,7 +35,6 @@
#include "components/autofill/core/common/mojom/autofill_types.mojom.h"
#include "components/autofill/core/common/password_form_fill_data.h"
#include "components/autofill/core/common/signatures.h"
-#include "components/password_manager/core/common/password_manager_features.h"
#include "components/safe_browsing/buildflags.h"
#include "content/public/renderer/document_state.h"
#include "content/public/renderer/render_frame.h"
@@ -257,9 +255,9 @@ WebString GetFormSignatureAsWebString(const FormData& form_data) {
// Annotate |fields| with field signatures and form signature as HTML
// attributes.
void AnnotateFieldsWithSignatures(
- std::vector<blink::WebFormControlElement>* fields,
+ std::vector<blink::WebFormControlElement>& fields,
const blink::WebString& form_signature) {
- for (blink::WebFormControlElement& control_element : *fields) {
+ for (blink::WebFormControlElement& control_element : fields) {
FieldSignature field_signature = CalculateFieldSignatureByNameAndType(
control_element.NameForAutofill().Utf16(),
control_element.FormControlTypeForAutofill().Utf8());
@@ -272,37 +270,6 @@ void AnnotateFieldsWithSignatures(
}
}
-// Annotate |forms| and all fields in the |frame| with form and field signatures
-// as HTML attributes.
-void AnnotateFormsAndFieldsWithSignatures(WebLocalFrame* frame,
- WebVector<WebFormElement>* forms) {
- for (WebFormElement& form : *forms) {
- std::unique_ptr<FormData> form_data(
- CreateFormDataFromWebForm(form, /*field_data_manager=*/nullptr,
- /*username_detector_cache=*/nullptr));
- WebString form_signature;
- if (form_data) {
- form_signature = GetFormSignatureAsWebString(*form_data);
- form.SetAttribute(WebString::FromASCII(kDebugAttributeForFormSignature),
- form_signature);
- }
- std::vector<WebFormControlElement> form_fields =
- form_util::ExtractAutofillableElementsInForm(form);
- AnnotateFieldsWithSignatures(&form_fields, form_signature);
- }
-
- std::vector<WebFormControlElement> unowned_elements =
- form_util::GetUnownedAutofillableFormFieldElements(
- frame->GetDocument().All(), nullptr);
- std::unique_ptr<FormData> form_data(CreateFormDataFromUnownedInputElements(
- *frame, /*field_data_manager=*/nullptr,
- /*username_detector_cache=*/nullptr));
- WebString form_signature;
- if (form_data)
- form_signature = GetFormSignatureAsWebString(*form_data);
- AnnotateFieldsWithSignatures(&unowned_elements, form_signature);
-}
-
// Returns true iff there is a password field in |frame|.
bool HasPasswordField(const WebLocalFrame& frame) {
static base::NoDestructor<WebString> kPassword("password");
@@ -416,14 +383,6 @@ bool HasDocumentWithValidFrame(const WebInputElement& element) {
return frame && frame->View();
}
-bool ShowPopupWithoutPasswords(const WebInputElement& password_element) {
- if (!base::FeatureList::IsEnabled(
- password_manager::features::kEnablePasswordsAccountStorage)) {
- return false;
- }
- return !password_element.IsNull() && IsElementEditable(password_element);
-}
-
// This method tries to fix `fields` with empty typed or filled properties by
// matching them against previously filled or typed in fields with the same
// value and copying their filled or typed mask.
@@ -905,9 +864,10 @@ bool PasswordAutofillAgent::ShowSuggestions(
FindPasswordInfoForElement(element, UseFallbackData(true), &username_element,
&password_element, &password_info);
- if (!password_info && !ShowPopupWithoutPasswords(password_element)) {
+ if (!password_info) {
MaybeCheckSafeBrowsingReputation(element);
- return false;
+ if (!CanShowPopupWithoutPasswords(password_element))
+ return false;
}
// Check that all fillable elements are editable.
@@ -1015,6 +975,31 @@ void PasswordAutofillAgent::UserGestureObserved() {
gatekeeper_.OnUserGesture();
}
+void PasswordAutofillAgent::AnnotateFormsAndFieldsWithSignatures(
+ WebVector<WebFormElement>& forms) {
+ for (WebFormElement& form : forms) {
+ std::unique_ptr<FormData> form_data = GetFormDataFromWebForm(form);
+ WebString form_signature;
+ if (form_data) {
+ form_signature = GetFormSignatureAsWebString(*form_data);
+ form.SetAttribute(WebString::FromASCII(kDebugAttributeForFormSignature),
+ form_signature);
+ }
+ std::vector<WebFormControlElement> form_fields =
+ form_util::ExtractAutofillableElementsInForm(form);
+ AnnotateFieldsWithSignatures(form_fields, form_signature);
+ }
+
+ std::vector<WebFormControlElement> unowned_elements =
+ form_util::GetUnownedAutofillableFormFieldElements(
+ render_frame()->GetWebFrame()->GetDocument().All(), nullptr);
+ std::unique_ptr<FormData> form_data = GetFormDataFromUnownedInputElements();
+ WebString form_signature;
+ if (form_data)
+ form_signature = GetFormSignatureAsWebString(*form_data);
+ AnnotateFieldsWithSignatures(unowned_elements, form_signature);
+}
+
void PasswordAutofillAgent::SendPasswordForms(bool only_visible) {
std::unique_ptr<RendererSavePasswordProgressLogger> logger;
if (logging_state_active_) {
@@ -1046,7 +1031,7 @@ void PasswordAutofillAgent::SendPasswordForms(bool only_visible) {
WebVector<WebFormElement> forms = frame->GetDocument().Forms();
if (IsShowAutofillSignaturesEnabled())
- AnnotateFormsAndFieldsWithSignatures(frame, &forms);
+ AnnotateFormsAndFieldsWithSignatures(forms);
if (logger)
logger->LogNumber(Logger::STRING_NUMBER_OF_ALL_FORMS, forms.size());
@@ -1131,7 +1116,7 @@ void PasswordAutofillAgent::SendPasswordForms(bool only_visible) {
password_forms_data.back().url =
form_util::GetCanonicalOriginForDocument(frame->GetDocument());
password_forms_data.back().full_url =
- form_util::GetOriginWithoutAuthForDocument(frame->GetDocument());
+ form_util::GetDocumentUrlWithoutAuth(frame->GetDocument());
}
if (!password_forms_data.empty()) {
sent_request_to_store_ = true;
@@ -1161,12 +1146,9 @@ void PasswordAutofillAgent::DidFinishLoad() {
}
void PasswordAutofillAgent::DidCommitProvisionalLoad(
- bool is_same_document_navigation,
ui::PageTransition transition) {
- if (!is_same_document_navigation) {
- checked_safe_browsing_reputation_ = false;
- recorded_first_filling_result_ = false;
- }
+ checked_safe_browsing_reputation_ = false;
+ recorded_first_filling_result_ = false;
}
void PasswordAutofillAgent::OnFrameDetached() {
@@ -1312,7 +1294,10 @@ void PasswordAutofillAgent::AnnotateFieldsWithParsingResult(
"confirmation_password_element");
}
-void PasswordAutofillAgent::InformNoSavedCredentials() {
+void PasswordAutofillAgent::InformNoSavedCredentials(
+ bool should_show_popup_without_passwords) {
+ should_show_popup_without_passwords_ = should_show_popup_without_passwords;
+
autofilled_elements_cache_.clear();
// Clear the actual field values.
@@ -1382,7 +1367,8 @@ void PasswordAutofillAgent::FocusedNodeHasChanged(const blink::WebNode& node) {
std::unique_ptr<FormData> PasswordAutofillAgent::GetFormDataFromWebForm(
const WebFormElement& web_form) {
return CreateFormDataFromWebForm(web_form, field_data_manager_.get(),
- &username_detector_cache_);
+ &username_detector_cache_,
+ &button_titles_cache_);
}
std::unique_ptr<FormData>
@@ -1398,7 +1384,8 @@ PasswordAutofillAgent::GetFormDataFromUnownedInputElements() {
if (!web_frame)
return nullptr;
return CreateFormDataFromUnownedInputElements(
- *web_frame, field_data_manager_.get(), &username_detector_cache_);
+ *web_frame, field_data_manager_.get(), &username_detector_cache_,
+ &button_titles_cache_);
}
////////////////////////////////////////////////////////////////////////////////
@@ -1430,6 +1417,7 @@ void PasswordAutofillAgent::CleanupOnDocumentShutdown() {
web_input_to_password_info_.clear();
password_to_username_.clear();
last_supplied_password_info_iter_ = web_input_to_password_info_.end();
+ should_show_popup_without_passwords_ = false;
browser_has_form_to_process_ = false;
field_data_manager_.get()->ClearData();
username_autofill_state_ = WebAutofillState::kNotFilled;
@@ -1535,7 +1523,7 @@ bool PasswordAutofillAgent::FillUserNameAndPassword(
// This is a heuristic guess. If the credential is stored for
// www.example.com, the username may be prefilled with "@example.com".
std::string possible_email_domain =
- GetRegistryControlledDomain(fill_data.origin);
+ GetRegistryControlledDomain(fill_data.url);
prefilled_placeholder_username =
!username_element.Value().IsEmpty() &&
@@ -1886,4 +1874,10 @@ void PasswordAutofillAgent::SetLastUpdatedFormAndField(
: FieldRendererId(input.UniqueRendererFormControlId());
}
+bool PasswordAutofillAgent::CanShowPopupWithoutPasswords(
+ const WebInputElement& password_element) const {
+ return should_show_popup_without_passwords_ && !password_element.IsNull() &&
+ IsElementEditable(password_element);
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/content/renderer/password_autofill_agent.h b/chromium/components/autofill/content/renderer/password_autofill_agent.h
index 011425f9186..4852ec2a3b8 100644
--- a/chromium/components/autofill/content/renderer/password_autofill_agent.h
+++ b/chromium/components/autofill/content/renderer/password_autofill_agent.h
@@ -20,6 +20,7 @@
#include "components/autofill/content/common/mojom/autofill_driver.mojom.h"
#include "components/autofill/content/renderer/autofill_agent.h"
#include "components/autofill/content/renderer/field_data_manager.h"
+#include "components/autofill/content/renderer/form_autofill_util.h"
#include "components/autofill/content/renderer/form_tracker.h"
#include "components/autofill/content/renderer/html_based_username_detector.h"
#include "components/autofill/core/common/mojom/autofill_types.mojom.h"
@@ -99,10 +100,6 @@ enum class FillingResult {
kMaxValue = kNoFillableElementsFound,
};
-// Names of HTML attributes to show form and field signatures for debugging.
-extern const char kDebugAttributeForFormSignature[];
-extern const char kDebugAttributeForFieldSignature[];
-
class FieldDataManager;
class RendererSavePasswordProgressLogger;
class PasswordGenerationAgent;
@@ -133,7 +130,8 @@ class PasswordAutofillAgent : public content::RenderFrameObserver,
// mojom::PasswordAutofillAgent:
void FillPasswordForm(const PasswordFormFillData& form_data) override;
- void InformNoSavedCredentials() override;
+ void InformNoSavedCredentials(
+ bool should_show_popup_without_passwords) override;
void FillIntoFocusedField(bool is_password,
const base::string16& credential) override;
void SetLoggingState(bool active) override;
@@ -235,8 +233,7 @@ class PasswordAutofillAgent : public content::RenderFrameObserver,
void DidFinishLoad() override;
void ReadyToCommitNavigation(
blink::WebDocumentLoader* document_loader) override;
- void DidCommitProvisionalLoad(bool is_same_document_navigation,
- ui::PageTransition transition) override;
+ void DidCommitProvisionalLoad(ui::PageTransition transition) override;
void OnDestruct() override;
const scoped_refptr<FieldDataManager> GetFieldDataManager() {
@@ -345,6 +342,12 @@ class PasswordAutofillAgent : public content::RenderFrameObserver,
DISALLOW_COPY_AND_ASSIGN(PasswordValueGatekeeper);
};
+ // Annotate |forms| and all fields in the current frame with form and field
+ // signatures as HTML attributes. Used by
+ // chrome://flags/#enable-show-autofill-signatures only.
+ void AnnotateFormsAndFieldsWithSignatures(
+ blink::WebVector<blink::WebFormElement>& forms);
+
// Scans the given frame for password forms and sends them up to the browser.
// If |only_visible| is true, only forms visible in the layout are sent.
void SendPasswordForms(bool only_visible);
@@ -467,6 +470,9 @@ class PasswordAutofillAgent : public content::RenderFrameObserver,
void SetLastUpdatedFormAndField(const blink::WebFormElement& form,
const blink::WebFormControlElement& input);
+ bool CanShowPopupWithoutPasswords(
+ const blink::WebInputElement& password_element) const;
+
// The logins we have filled so far with their associated info.
WebInputToPasswordInfoMap web_input_to_password_info_;
// A (sort-of) reverse map to |web_input_to_password_info_|.
@@ -474,6 +480,8 @@ class PasswordAutofillAgent : public content::RenderFrameObserver,
// The chronologically last insertion into |web_input_to_password_info_|.
WebInputToPasswordInfoMap::iterator last_supplied_password_info_iter_;
+ bool should_show_popup_without_passwords_ = false;
+
// Map WebFormControlElement to the pair of:
// 1) The most recent text that user typed or PasswordManager autofilled in
// input elements. Used for storing username/password before JavaScript
@@ -513,10 +521,6 @@ class PasswordAutofillAgent : public content::RenderFrameObserver,
// Records the username typed before suggestions preview.
base::string16 username_query_prefix_;
- // The HTML based username detector's cache which maps form elements to
- // username predictions.
- UsernameDetectorCache username_detector_cache_;
-
// This notifier is used to avoid sending redundant messages to the password
// manager driver mojo interface.
FocusStateNotifier focus_state_notifier_;
@@ -543,6 +547,14 @@ class PasswordAutofillAgent : public content::RenderFrameObserver,
// structure. Replace FormData with a smaller structure.
std::map<FormRendererId, FormStructureInfo> forms_structure_cache_;
+ // The HTML based username detector's cache which maps form elements to
+ // username predictions.
+ UsernameDetectorCache username_detector_cache_;
+
+ // Stores the mapping from a form element's ID to results of button titles
+ // heuristics for that form.
+ form_util::ButtonTitlesCache button_titles_cache_;
+
// Flag to prevent that multiple PasswordManager.FirstRendererFillingResult
// UMA metrics are recorded per page load. This is reset on
// DidCommitProvisionalLoad() but only for non-same-document-navigations.
diff --git a/chromium/components/autofill/content/renderer/password_form_conversion_utils.cc b/chromium/components/autofill/content/renderer/password_form_conversion_utils.cc
index a216162bcfd..93824cae0bd 100644
--- a/chromium/components/autofill/content/renderer/password_form_conversion_utils.cc
+++ b/chromium/components/autofill/content/renderer/password_form_conversion_utils.cc
@@ -9,7 +9,6 @@
#include "base/no_destructor.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_split.h"
-#include "components/autofill/content/renderer/form_autofill_util.h"
#include "components/autofill/content/renderer/html_based_username_detector.h"
#include "components/autofill/core/common/password_form.h"
#include "components/autofill/core/common/renderer_id.h"
@@ -128,7 +127,8 @@ bool IsGaiaWithSkipSavePasswordForm(const blink::WebFormElement& form) {
std::unique_ptr<FormData> CreateFormDataFromWebForm(
const WebFormElement& web_form,
const FieldDataManager* field_data_manager,
- UsernameDetectorCache* username_detector_cache) {
+ UsernameDetectorCache* username_detector_cache,
+ form_util::ButtonTitlesCache* button_titles_cache) {
if (web_form.IsNull())
return nullptr;
@@ -136,7 +136,7 @@ std::unique_ptr<FormData> CreateFormDataFromWebForm(
form_data->url =
form_util::GetCanonicalOriginForDocument(web_form.GetDocument());
form_data->full_url =
- form_util::GetOriginWithoutAuthForDocument(web_form.GetDocument());
+ form_util::GetDocumentUrlWithoutAuth(web_form.GetDocument());
form_data->is_gaia_with_skip_save_password_form =
IsGaiaWithSkipSavePasswordForm(web_form) ||
IsGaiaReauthenticationForm(web_form);
@@ -153,6 +153,8 @@ std::unique_ptr<FormData> CreateFormDataFromWebForm(
}
form_data->username_predictions = GetUsernamePredictions(
control_elements.ReleaseVector(), *form_data, username_detector_cache);
+ form_data->button_titles = form_util::GetButtonTitles(
+ web_form, web_form.GetDocument(), button_titles_cache);
return form_data;
}
@@ -160,7 +162,8 @@ std::unique_ptr<FormData> CreateFormDataFromWebForm(
std::unique_ptr<FormData> CreateFormDataFromUnownedInputElements(
const WebLocalFrame& frame,
const FieldDataManager* field_data_manager,
- UsernameDetectorCache* username_detector_cache) {
+ UsernameDetectorCache* username_detector_cache,
+ form_util::ButtonTitlesCache* button_titles_cache) {
std::vector<WebElement> fieldsets;
std::vector<WebFormControlElement> control_elements =
form_util::GetUnownedFormFieldElements(frame.GetDocument().All(),
@@ -179,9 +182,12 @@ std::unique_ptr<FormData> CreateFormDataFromUnownedInputElements(
form_data->url =
form_util::GetCanonicalOriginForDocument(frame.GetDocument());
form_data->full_url =
- form_util::GetOriginWithoutAuthForDocument(frame.GetDocument());
+ form_util::GetDocumentUrlWithoutAuth(frame.GetDocument());
form_data->username_predictions = GetUsernamePredictions(
control_elements, *form_data, username_detector_cache);
+ form_data->button_titles = form_util::GetButtonTitles(
+ WebFormElement(), frame.GetDocument(), button_titles_cache);
+
return form_data;
}
diff --git a/chromium/components/autofill/content/renderer/password_form_conversion_utils.h b/chromium/components/autofill/content/renderer/password_form_conversion_utils.h
index 8701d51e57e..07a2ad05095 100644
--- a/chromium/components/autofill/content/renderer/password_form_conversion_utils.h
+++ b/chromium/components/autofill/content/renderer/password_form_conversion_utils.h
@@ -12,6 +12,7 @@
#include <vector>
#include "base/strings/string_piece.h"
+#include "components/autofill/content/renderer/form_autofill_util.h"
#include "components/autofill/content/renderer/html_based_username_detector.h"
#include "components/autofill/core/common/password_form.h"
#include "third_party/blink/public/platform/web_string.h"
@@ -42,14 +43,16 @@ bool IsGaiaWithSkipSavePasswordForm(const blink::WebFormElement& form);
std::unique_ptr<FormData> CreateFormDataFromWebForm(
const blink::WebFormElement& web_form,
const FieldDataManager* field_data_manager,
- UsernameDetectorCache* username_detector_cache);
+ UsernameDetectorCache* username_detector_cache,
+ form_util::ButtonTitlesCache* button_titles_cache);
// Same as CreateFormDataFromWebForm() but for input elements that are
// not enclosed in <form> element.
std::unique_ptr<FormData> CreateFormDataFromUnownedInputElements(
const blink::WebLocalFrame& frame,
const FieldDataManager* field_data_manager,
- UsernameDetectorCache* username_detector_cache);
+ UsernameDetectorCache* username_detector_cache,
+ form_util::ButtonTitlesCache* button_titles_cache);
// The "Realm" for the sign-on. This is scheme, host, port.
std::string GetSignOnRealm(const GURL& origin);
diff --git a/chromium/components/autofill/content/renderer/password_generation_agent.cc b/chromium/components/autofill/content/renderer/password_generation_agent.cc
index 959fa2d7388..a87d298ca38 100644
--- a/chromium/components/autofill/content/renderer/password_generation_agent.cc
+++ b/chromium/components/autofill/content/renderer/password_generation_agent.cc
@@ -160,10 +160,7 @@ void PasswordGenerationAgent::BindPendingReceiver(
}
void PasswordGenerationAgent::DidCommitProvisionalLoad(
- bool is_same_document_navigation,
ui::PageTransition transition) {
- if (is_same_document_navigation)
- return;
// Update stats for main frame navigation.
if (!render_frame()->GetWebFrame()->Parent()) {
if (current_generation_item_) {
diff --git a/chromium/components/autofill/content/renderer/password_generation_agent.h b/chromium/components/autofill/content/renderer/password_generation_agent.h
index 6baf4393658..2eeb0e3044e 100644
--- a/chromium/components/autofill/content/renderer/password_generation_agent.h
+++ b/chromium/components/autofill/content/renderer/password_generation_agent.h
@@ -97,8 +97,7 @@ class PasswordGenerationAgent : public content::RenderFrameObserver,
struct GenerationItemInfo;
// RenderFrameObserver:
- void DidCommitProvisionalLoad(bool is_same_document_navigation,
- ui::PageTransition transition) override;
+ void DidCommitProvisionalLoad(ui::PageTransition transition) override;
void DidChangeScrollOffset() override;
void OnDestruct() override;