diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-01-23 17:21:03 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-01-23 16:25:15 +0000 |
commit | c551f43206405019121bd2b2c93714319a0a3300 (patch) | |
tree | 1f48c30631c421fd4bbb3c36da20183c8a2ed7d7 /chromium/components/autofill | |
parent | 7961cea6d1041e3e454dae6a1da660b453efd238 (diff) | |
download | qtwebengine-chromium-c551f43206405019121bd2b2c93714319a0a3300.tar.gz |
BASELINE: Update Chromium to 79.0.3945.139
Change-Id: I336b7182fab9bca80b709682489c07db112eaca5
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/components/autofill')
230 files changed, 6296 insertions, 7196 deletions
diff --git a/chromium/components/autofill/android/BUILD.gn b/chromium/components/autofill/android/BUILD.gn index 88c3ffd4b58..3313c23417b 100644 --- a/chromium/components/autofill/android/BUILD.gn +++ b/chromium/components/autofill/android/BUILD.gn @@ -100,10 +100,12 @@ android_library("autofill_java") { android_library("provider_java") { deps = [ "//base:base_java", + "//base:jni_java", "//components/autofill/core/common/mojom:mojo_types_java", "//content/public/android:content_java", - "//third_party/android_deps:com_android_support_support_annotations_java", + "//third_party/android_deps:androidx_annotation_annotation_java", ] + annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ] java_files = [ "java/src/org/chromium/components/autofill/AutofillProvider.java", "java/src/org/chromium/components/autofill/FormData.java", diff --git a/chromium/components/autofill/android/java/src/org/chromium/components/autofill/AutofillProvider.java b/chromium/components/autofill/android/java/src/org/chromium/components/autofill/AutofillProvider.java index 0937a500970..a7d05d359a4 100644 --- a/chromium/components/autofill/android/java/src/org/chromium/components/autofill/AutofillProvider.java +++ b/chromium/components/autofill/android/java/src/org/chromium/components/autofill/AutofillProvider.java @@ -11,6 +11,7 @@ import android.view.autofill.AutofillValue; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; +import org.chromium.base.annotations.NativeMethods; import org.chromium.content_public.browser.WebContents; /** @@ -132,7 +133,8 @@ public abstract class AutofillProvider { * @param formData the form to fill. */ protected void autofill(long nativeAutofillProvider, FormData formData) { - nativeOnAutofillAvailable(nativeAutofillProvider, formData); + AutofillProviderJni.get().onAutofillAvailable( + nativeAutofillProvider, AutofillProvider.this, formData); } /** @@ -147,6 +149,9 @@ public abstract class AutofillProvider { @CalledByNative protected abstract void onDidFillAutofillFormData(); - private native void nativeOnAutofillAvailable( - long nativeAutofillProviderAndroid, FormData formData); + @NativeMethods + interface Natives { + void onAutofillAvailable( + long nativeAutofillProviderAndroid, AutofillProvider caller, FormData formData); + } } diff --git a/chromium/components/autofill/android/java/src/org/chromium/components/autofill/FormData.java b/chromium/components/autofill/android/java/src/org/chromium/components/autofill/FormData.java index ca1362a286a..5c4db2d1bdb 100644 --- a/chromium/components/autofill/android/java/src/org/chromium/components/autofill/FormData.java +++ b/chromium/components/autofill/android/java/src/org/chromium/components/autofill/FormData.java @@ -6,6 +6,7 @@ package org.chromium.components.autofill; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; +import org.chromium.base.annotations.NativeMethods; import java.util.ArrayList; @@ -35,10 +36,11 @@ public class FormData { } private void popupFormFields(int fieldCount) { - FormFieldData formFieldData = nativeGetNextFormFieldData(mNativeObj); + FormFieldData formFieldData = + FormDataJni.get().getNextFormFieldData(mNativeObj, FormData.this); while (formFieldData != null) { mFields.add(formFieldData); - formFieldData = nativeGetNextFormFieldData(mNativeObj); + formFieldData = FormDataJni.get().getNextFormFieldData(mNativeObj, FormData.this); } assert mFields.size() == fieldCount; } @@ -48,5 +50,8 @@ public class FormData { mNativeObj = 0; } - private native FormFieldData nativeGetNextFormFieldData(long nativeFormDataAndroid); + @NativeMethods + interface Natives { + FormFieldData getNextFormFieldData(long nativeFormDataAndroid, FormData caller); + } } diff --git a/chromium/components/autofill/android/java/src/org/chromium/components/autofill/FormFieldData.java b/chromium/components/autofill/android/java/src/org/chromium/components/autofill/FormFieldData.java index aa4a395dbda..9a95d0a775a 100644 --- a/chromium/components/autofill/android/java/src/org/chromium/components/autofill/FormFieldData.java +++ b/chromium/components/autofill/android/java/src/org/chromium/components/autofill/FormFieldData.java @@ -4,7 +4,7 @@ package org.chromium.components.autofill; -import android.support.annotation.IntDef; +import androidx.annotation.IntDef; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; diff --git a/chromium/components/autofill/content/browser/content_autofill_driver.cc b/chromium/components/autofill/content/browser/content_autofill_driver.cc index 68239ae09f5..9d2e0b76391 100644 --- a/chromium/components/autofill/content/browser/content_autofill_driver.cc +++ b/chromium/components/autofill/content/browser/content_autofill_driver.cc @@ -176,10 +176,10 @@ void ContentAutofillDriver::RendererShouldPreviewFieldWithValue( } void ContentAutofillDriver::RendererShouldSetSuggestionAvailability( - bool available) { + const mojom::AutofillState state) { if (!RendererIsAvailable()) return; - GetAutofillAgent()->SetSuggestionAvailability(available); + GetAutofillAgent()->SetSuggestionAvailability(state); } void ContentAutofillDriver::PopupHidden() { diff --git a/chromium/components/autofill/content/browser/content_autofill_driver.h b/chromium/components/autofill/content/browser/content_autofill_driver.h index c2496dc87b0..abe86a99845 100644 --- a/chromium/components/autofill/content/browser/content_autofill_driver.h +++ b/chromium/components/autofill/content/browser/content_autofill_driver.h @@ -7,6 +7,7 @@ #include <memory> #include <string> +#include <vector> #include "base/supports_user_data.h" #include "build/build_config.h" @@ -24,7 +25,7 @@ namespace content { class NavigationHandle; class RenderFrameHost; -} +} // namespace content namespace autofill { @@ -77,7 +78,8 @@ class ContentAutofillDriver : public AutofillDriver, void RendererShouldFillFieldWithValue(const base::string16& value) override; void RendererShouldPreviewFieldWithValue( const base::string16& value) override; - void RendererShouldSetSuggestionAvailability(bool available) override; + void RendererShouldSetSuggestionAvailability( + const mojom::AutofillState state) override; void PopupHidden() override; gfx::RectF TransformBoundingBoxToViewportCoordinates( const gfx::RectF& bounding_box) override; @@ -120,10 +122,6 @@ class ContentAutofillDriver : public AutofillDriver, // subframe navigations. See navigation_handle.h for details. void DidNavigateMainFrame(content::NavigationHandle* navigation_handle); - AutofillExternalDelegate* autofill_external_delegate() { - return autofill_external_delegate_.get(); - } - AutofillManager* autofill_manager() { return autofill_manager_; } AutofillHandler* autofill_handler() { return autofill_handler_.get(); } content::RenderFrameHost* render_frame_host() { return render_frame_host_; } diff --git a/chromium/components/autofill/content/browser/content_autofill_driver_factory.cc b/chromium/components/autofill/content/browser/content_autofill_driver_factory.cc index e4dece7c37d..a34b85e8edc 100644 --- a/chromium/components/autofill/content/browser/content_autofill_driver_factory.cc +++ b/chromium/components/autofill/content/browser/content_autofill_driver_factory.cc @@ -57,17 +57,10 @@ void ContentAutofillDriverFactory::CreateForWebContentsAndDelegate( if (FromWebContents(contents)) return; - auto new_factory = std::make_unique<ContentAutofillDriverFactory>( - contents, client, app_locale, enable_download_manager, provider); - const std::vector<content::RenderFrameHost*> frames = - contents->GetAllFrames(); - for (content::RenderFrameHost* frame : frames) { - if (frame->IsRenderFrameLive()) - new_factory->RenderFrameCreated(frame); - } - - contents->SetUserData(kContentAutofillDriverFactoryWebContentsUserDataKey, - std::move(new_factory)); + contents->SetUserData( + kContentAutofillDriverFactoryWebContentsUserDataKey, + std::make_unique<ContentAutofillDriverFactory>( + contents, client, app_locale, enable_download_manager, provider)); } // static @@ -115,17 +108,20 @@ ContentAutofillDriverFactory::ContentAutofillDriverFactory( ContentAutofillDriver* ContentAutofillDriverFactory::DriverForFrame( content::RenderFrameHost* render_frame_host) { + AutofillDriver* driver = DriverForKey(render_frame_host); + + // ContentAutofillDriver are created on demand here. + if (!driver) { + AddForKey(render_frame_host, + base::Bind(CreateDriver, render_frame_host, client(), app_locale_, + enable_download_manager_, provider_)); + driver = DriverForKey(render_frame_host); + } + // This cast is safe because AutofillDriverFactory::AddForKey is protected // and always called with ContentAutofillDriver instances within // ContentAutofillDriverFactory. - return static_cast<ContentAutofillDriver*>(DriverForKey(render_frame_host)); -} - -void ContentAutofillDriverFactory::RenderFrameCreated( - content::RenderFrameHost* render_frame_host) { - AddForKey(render_frame_host, - base::Bind(CreateDriver, render_frame_host, client(), app_locale_, - enable_download_manager_, provider_)); + return static_cast<ContentAutofillDriver*>(driver); } void ContentAutofillDriverFactory::RenderFrameDeleted( diff --git a/chromium/components/autofill/content/browser/content_autofill_driver_factory.h b/chromium/components/autofill/content/browser/content_autofill_driver_factory.h index a530737bd8c..ddd66b77b69 100644 --- a/chromium/components/autofill/content/browser/content_autofill_driver_factory.h +++ b/chromium/components/autofill/content/browser/content_autofill_driver_factory.h @@ -63,7 +63,6 @@ class ContentAutofillDriverFactory : public AutofillDriverFactory, content::RenderFrameHost* render_frame_host); // content::WebContentsObserver: - void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override; void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override; void DidFinishNavigation( content::NavigationHandle* navigation_handle) override; diff --git a/chromium/components/autofill/content/browser/content_autofill_driver_unittest.cc b/chromium/components/autofill/content/browser/content_autofill_driver_unittest.cc index 547fbd9d9c1..9815b85401a 100644 --- a/chromium/components/autofill/content/browser/content_autofill_driver_unittest.cc +++ b/chromium/components/autofill/content/browser/content_autofill_driver_unittest.cc @@ -188,8 +188,11 @@ class FakeAutofillAgent : public mojom::AutofillAgent { CallDone(); } - void SetSuggestionAvailability(bool value) override { - suggestions_available_ = value; + void SetSuggestionAvailability(const mojom::AutofillState state) override { + if (state == mojom::AutofillState::kAutofillAvailable) + suggestions_available_ = true; + else if (state == mojom::AutofillState::kNoSuggestions) + suggestions_available_ = false; CallDone(); } @@ -204,9 +207,6 @@ class FakeAutofillAgent : public mojom::AutofillAgent { void PreviewPasswordSuggestion(const base::string16& username, const base::string16& password) override {} - void ShowInitialPasswordAccountSuggestions( - const PasswordFormFillData& form_data) override {} - void SetUserGestureRequired(bool required) override {} void SetSecureContextRequired(bool required) override {} diff --git a/chromium/components/autofill/content/browser/risk/fingerprint.cc b/chromium/components/autofill/content/browser/risk/fingerprint.cc index e640bf8c7bd..fd6b9ab2765 100644 --- a/chromium/components/autofill/content/browser/risk/fingerprint.cc +++ b/chromium/components/autofill/content/browser/risk/fingerprint.cc @@ -28,6 +28,7 @@ #include "base/timer/timer.h" #include "base/values.h" #include "components/autofill/content/browser/risk/proto/fingerprint.pb.h" +#include "components/autofill/core/common/autofill_clock.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/font_list_async.h" #include "content/public/browser/gpu_data_manager.h" @@ -40,6 +41,7 @@ #include "content/public/common/webplugininfo.h" #include "gpu/config/gpu_info.h" #include "mojo/public/cpp/bindings/interface_request.h" +#include "mojo/public/cpp/bindings/remote.h" #include "ppapi/buildflags/buildflags.h" #include "services/device/public/cpp/geolocation/geoposition.h" #include "services/device/public/mojom/constants.mojom.h" @@ -65,7 +67,7 @@ const int kTimeoutSeconds = 4; // Returns the delta between the local timezone and UTC. base::TimeDelta GetTimezoneOffset() { - const base::Time utc = base::Time::Now(); + const base::Time utc = AutofillClock::Now(); base::Time::Exploded local; utc.LocalExplode(&local); @@ -81,7 +83,7 @@ base::TimeDelta GetTimezoneOffset() { // "Mac OS X 10.6.8". std::string GetOperatingSystemVersion() { return base::SysInfo::OperatingSystemName() + " " + - base::SysInfo::OperatingSystemVersion(); + base::SysInfo::OperatingSystemVersion(); } // Adds the list of |fonts| to the |machine|. @@ -106,8 +108,7 @@ void AddFontsToFingerprint(const base::ListValue& fonts, void AddPluginsToFingerprint(const std::vector<content::WebPluginInfo>& plugins, Fingerprint::MachineCharacteristics* machine) { for (const content::WebPluginInfo& it : plugins) { - Fingerprint::MachineCharacteristics::Plugin* plugin = - machine->add_plugin(); + Fingerprint::MachineCharacteristics::Plugin* plugin = machine->add_plugin(); plugin->set_name(base::UTF16ToUTF8(it.name)); plugin->set_description(base::UTF16ToUTF8(it.desc)); for (const content::WebPluginMimeType& mime_type : it.mime_types) @@ -241,8 +242,8 @@ class FingerprintDataLoader : public content::GpuDataManagerObserver { std::vector<content::WebPluginInfo> plugins_; bool waiting_on_plugins_; device::mojom::Geoposition geoposition_; - device::mojom::GeolocationPtr geolocation_; - device::mojom::GeolocationContextPtr geolocation_context_; + mojo::Remote<device::mojom::Geolocation> geolocation_; + mojo::Remote<device::mojom::GeolocationContext> geolocation_context_; // Timer to enforce a maximum timeout before the |callback_| is called, even // if not all asynchronous data has been loaded. @@ -296,7 +297,7 @@ FingerprintDataLoader::FingerprintDataLoader( if (gpu_data_manager_->GpuAccessAllowed(nullptr) && !gpu_data_manager_->IsEssentialGpuInfoAvailable()) { gpu_observer_.Add(gpu_data_manager_); - gpu_data_manager_->RequestCompleteGpuInfoIfNeeded(); + OnGpuInfoUpdate(); } #if BUILDFLAG(ENABLE_PLUGINS) @@ -313,9 +314,10 @@ FingerprintDataLoader::FingerprintDataLoader( // Load geolocation data. DCHECK(connector); - connector->BindInterface(device::mojom::kServiceName, - mojo::MakeRequest(&geolocation_context_)); - geolocation_context_->BindGeolocation(mojo::MakeRequest(&geolocation_)); + connector->Connect(device::mojom::kServiceName, + geolocation_context_.BindNewPipeAndPassReceiver()); + geolocation_context_->BindGeolocation( + geolocation_.BindNewPipeAndPassReceiver()); geolocation_->SetHighAccuracy(false); geolocation_->QueryNextPosition( base::BindOnce(&FingerprintDataLoader::OnGotGeoposition, @@ -431,7 +433,7 @@ void FingerprintDataLoader::FillFingerprint() { Fingerprint::Metadata* metadata = fingerprint->mutable_metadata(); metadata->set_timestamp_ms( - (base::Time::Now() - base::Time::UnixEpoch()).InMilliseconds()); + (AutofillClock::Now() - base::Time::UnixEpoch()).InMilliseconds()); metadata->set_obfuscated_gaia_id(obfuscated_gaia_id_); metadata->set_fingerprinter_version(kFingerprinterVersion); diff --git a/chromium/components/autofill/content/browser/risk/fingerprint_browsertest.cc b/chromium/components/autofill/content/browser/risk/fingerprint_browsertest.cc index 8cff02245f7..5ce5460c35e 100644 --- a/chromium/components/autofill/content/browser/risk/fingerprint_browsertest.cc +++ b/chromium/components/autofill/content/browser/risk/fingerprint_browsertest.cc @@ -13,6 +13,7 @@ #include "base/run_loop.h" #include "build/build_config.h" #include "components/autofill/content/browser/risk/proto/fingerprint.pb.h" +#include "components/autofill/core/common/autofill_clock.h" #include "content/public/browser/gpu_data_manager.h" #include "content/public/browser/system_connector.h" #include "content/public/common/screen_info.h" @@ -211,7 +212,7 @@ IN_PROC_BROWSER_TEST_F(AutofillRiskFingerprintTest, GetFingerprint) { base::RunLoop run_loop; internal::GetFingerprintInternal( kObfuscatedGaiaId, window_bounds_, content_bounds_, screen_info, - "25.0.0.123", kCharset, kAcceptLanguages, base::Time::Now(), kLocale, + "25.0.0.123", kCharset, kAcceptLanguages, AutofillClock::Now(), kLocale, kUserAgent, base::TimeDelta::FromDays(1), // Ought to be longer than any test run. base::Bind(&AutofillRiskFingerprintTest::GetFingerprintTestCallback, diff --git a/chromium/components/autofill/content/common/mojom/autofill_agent.mojom b/chromium/components/autofill/content/common/mojom/autofill_agent.mojom index 2a80baca5cc..a1d4dc65d85 100644 --- a/chromium/components/autofill/content/common/mojom/autofill_agent.mojom +++ b/chromium/components/autofill/content/common/mojom/autofill_agent.mojom @@ -36,8 +36,8 @@ interface AutofillAgent { PreviewFieldWithValue(mojo_base.mojom.String16 value); // Sets the currently selected node's corresponding accessibility node's - // suggestion availability. - SetSuggestionAvailability(bool available); + // autofill/autocomplete suggestion availability. + SetSuggestionAvailability(AutofillState type); // Sets the currently selected node's value to be the given data list value. AcceptDataListSuggestion(mojo_base.mojom.String16 value); @@ -52,10 +52,6 @@ interface AutofillAgent { PreviewPasswordSuggestion(mojo_base.mojom.String16 username, mojo_base.mojom.String16 password); - // Sent when a password form is initially detected and suggestions should be - // shown. Used by the fill-on-select experiment. - ShowInitialPasswordAccountSuggestions(PasswordFormFillData form_data); - // Configures the render to require, or not, a user gesture before notifying // the autofill agent of a field change. The default is true. Bypassing the // user gesture check should only used for Android Webview, which needs to @@ -96,10 +92,13 @@ interface PasswordAutofillAgent { // logging the decisions made about saving the password. SetLoggingState(bool active); - // Sent when Autofill manager gets the query response from the Autofill server - // which contains information about username and password for some forms. - // |predictions| maps forms to their username fields. - AutofillUsernameAndPasswordDataReceived(FormsPredictionsMap predictions); + // Informs the renderer that the Touch To Fill sheet has been closed. + // Indicates whether the virtual keyboard should be shown instead. + TouchToFillClosed(bool show_virtual_keyboard); + + // Annotate password related (username, password) DOM input elements with + // corresponding HTML attributes. It is used only for debugging. + AnnotateFieldsWithParsingResult(ParsingResult parsing_result); }; // There is one instance of this interface per render frame in the render diff --git a/chromium/components/autofill/content/common/mojom/autofill_driver.mojom b/chromium/components/autofill/content/common/mojom/autofill_driver.mojom index b7fa28ae376..73e2536ca52 100644 --- a/chromium/components/autofill/content/common/mojom/autofill_driver.mojom +++ b/chromium/components/autofill/content/common/mojom/autofill_driver.mojom @@ -102,9 +102,12 @@ interface PasswordManagerDriver { // save/update prompt anchored to the omnibox icon should be removed. HideManualFallbackForSaving(); - // Notification that same-document navigation happened and at this moment we - // have filled password form. We use this as a signal for successful login. - SameDocumentNavigation(PasswordForm password_form); + // Notification that same-document navigation happened. We use this as a + // signal for successful login. + // TODO(crbug.com/949519): Rename this method to a more generic name since + // it's called in many scenarios other than SameDocumentNavigation such as + // FRAME_DETACHED. + SameDocumentNavigation(SubmissionIndicatorEvent submission_indication_event); // Sends |log| to browser for displaying to the user. Only strings passed as // an argument to methods overriding SavePasswordProgressLogger::SendLog may diff --git a/chromium/components/autofill/content/renderer/BUILD.gn b/chromium/components/autofill/content/renderer/BUILD.gn index 95d49af5ae5..f36cc9d156e 100644 --- a/chromium/components/autofill/content/renderer/BUILD.gn +++ b/chromium/components/autofill/content/renderer/BUILD.gn @@ -32,8 +32,6 @@ jumbo_static_library("renderer") { "password_generation_agent.h", "prefilled_values_detector.cc", "prefilled_values_detector.h", - "provisionally_saved_password_form.cc", - "provisionally_saved_password_form.h", "renderer_save_password_progress_logger.cc", "renderer_save_password_progress_logger.h", ] diff --git a/chromium/components/autofill/content/renderer/PRESUBMIT.py b/chromium/components/autofill/content/renderer/PRESUBMIT.py index 30f2ba54021..f17ca91e903 100644 --- a/chromium/components/autofill/content/renderer/PRESUBMIT.py +++ b/chromium/components/autofill/content/renderer/PRESUBMIT.py @@ -22,9 +22,9 @@ def _CheckNoDirectPasswordCalls(input_api, output_api): files.append(f) if len(files): - return [ output_api.PresubmitError( - 'Do not call IsPasswordField() or FormControlType() directly but ' + - 'use IsPasswordFieldForAutofill() and FormControlTypeForAutofill() ' + + return [ output_api.PresubmitPromptWarning( + 'Consider to not call IsPasswordField() or FormControlType() directly ' + + 'but use IsPasswordFieldForAutofill() and FormControlTypeForAutofill() ' + 'respectively. These declare text input fields as password fields ' + 'if they have been password fields in the past. This is relevant ' + 'for websites that allow to reveal passwords with a button that ' + diff --git a/chromium/components/autofill/content/renderer/autofill_agent.cc b/chromium/components/autofill/content/renderer/autofill_agent.cc index 5c479361950..e372068d96b 100644 --- a/chromium/components/autofill/content/renderer/autofill_agent.cc +++ b/chromium/components/autofill/content/renderer/autofill_agent.cc @@ -31,6 +31,7 @@ #include "components/autofill/core/common/autofill_data_validation.h" #include "components/autofill/core/common/autofill_features.h" #include "components/autofill/core/common/autofill_switches.h" +#include "components/autofill/core/common/autofill_tick_clock.h" #include "components/autofill/core/common/autofill_util.h" #include "components/autofill/core/common/form_data.h" #include "components/autofill/core/common/form_data_predictions.h" @@ -48,6 +49,7 @@ #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h" #include "third_party/blink/public/platform/web_keyboard_event.h" #include "third_party/blink/public/platform/web_url_request.h" +#include "third_party/blink/public/web/web_ax_enums.h" #include "third_party/blink/public/web/web_ax_object.h" #include "third_party/blink/public/web/web_console_message.h" #include "third_party/blink/public/web/web_document.h" @@ -219,8 +221,7 @@ void AutofillAgent::DidChangeScrollOffsetImpl( if (form_util::FindFormAndFieldForFormControlElement(element_, &form, &field)) { GetAutofillDriver()->TextFieldDidScroll( - form, field, - render_frame()->GetRenderView()->ElementBoundsInWindow(element_)); + form, field, render_frame()->ElementBoundsInWindow(element_)); } // Ignore subsequent scroll offset changes. @@ -269,8 +270,7 @@ void AutofillAgent::FocusedElementChanged(const WebElement& element) { if (form_util::FindFormAndFieldForFormControlElement(element_, &form, &field)) { GetAutofillDriver()->FocusOnFormField( - form, field, - render_frame()->GetRenderView()->ElementBoundsInWindow(element_)); + form, field, render_frame()->ElementBoundsInWindow(element_)); } } @@ -351,9 +351,8 @@ void AutofillAgent::OnTextFieldDidChange(const WebInputElement& element) { if (form_util::FindFormAndFieldForFormControlElement(element, &form, &field)) { GetAutofillDriver()->TextFieldDidChange( - form, field, - render_frame()->GetRenderView()->ElementBoundsInWindow(element), - base::TimeTicks::Now()); + form, field, render_frame()->ElementBoundsInWindow(element), + AutofillTickClock::NowTicks()); } } @@ -430,7 +429,7 @@ void AutofillAgent::TriggerRefillIfNeeded(const FormData& form) { if (form_util::FindFormAndFieldForFormControlElement(element_, &updated_form, &field) && (!element_.IsAutofilled() || !form.DynamicallySameFormAs(updated_form))) { - base::TimeTicks forms_seen_timestamp = base::TimeTicks::Now(); + base::TimeTicks forms_seen_timestamp = AutofillTickClock::NowTicks(); WebLocalFrame* frame = render_frame()->GetWebFrame(); std::vector<FormData> forms; forms.push_back(updated_form); @@ -460,7 +459,8 @@ void AutofillAgent::FillForm(int32_t id, const FormData& form) { if (!element_.Form().IsNull()) UpdateLastInteractedForm(element_.Form()); - GetAutofillDriver()->DidFillAutofillFormData(form, base::TimeTicks::Now()); + GetAutofillDriver()->DidFillAutofillFormData(form, + AutofillTickClock::NowTicks()); TriggerRefillIfNeeded(form); } @@ -528,14 +528,32 @@ void AutofillAgent::PreviewFieldWithValue(const base::string16& value) { DoPreviewFieldWithValue(value, input_element); } -void AutofillAgent::SetSuggestionAvailability(bool available) { +void AutofillAgent::SetSuggestionAvailability( + const mojom::AutofillState state) { if (element_.IsNull()) return; WebInputElement* input_element = ToWebInputElement(&element_); - if (input_element) - WebAXObject::FromWebNode(*input_element) - .HandleAutofillStateChanged(available); + if (input_element) { + switch (state) { + case autofill::mojom::AutofillState::kAutofillAvailable: + WebAXObject::FromWebNode(*input_element) + .HandleAutofillStateChanged( + blink::WebAXAutofillState::kAutofillAvailable); + return; + case autofill::mojom::AutofillState::kAutocompleteAvailable: + WebAXObject::FromWebNode(*input_element) + .HandleAutofillStateChanged( + blink::WebAXAutofillState::kAutocompleteAvailable); + return; + case autofill::mojom::AutofillState::kNoSuggestions: + WebAXObject::FromWebNode(*input_element) + .HandleAutofillStateChanged( + blink::WebAXAutofillState::kNoSuggestions); + return; + } + NOTREACHED(); + } } void AutofillAgent::AcceptDataListSuggestion(const base::string16& value) { @@ -563,31 +581,6 @@ void AutofillAgent::PreviewPasswordSuggestion(const base::string16& username, DCHECK(handled); } -void AutofillAgent::ShowInitialPasswordAccountSuggestions( - const PasswordFormFillData& form_data) { - std::vector<blink::WebInputElement> elements; - std::unique_ptr<RendererSavePasswordProgressLogger> logger; - if (password_autofill_agent_->logging_state_active()) { - logger.reset(new RendererSavePasswordProgressLogger( - GetPasswordManagerDriver().get())); - logger->LogMessage(SavePasswordProgressLogger:: - STRING_ON_SHOW_INITIAL_PASSWORD_ACCOUNT_SUGGESTIONS); - } - password_autofill_agent_->GetFillableElementFromFormData( - form_data, logger.get(), &elements); - - // If wait_for_username is true, we don't want to initially show form options - // until the user types in a valid username. - if (form_data.wait_for_username) - return; - - ShowSuggestionsOptions options; - options.autofill_on_empty_values = true; - options.show_full_suggestion_list = true; - for (auto element : elements) - ShowSuggestions(element, options); -} - bool AutofillAgent::CollectFormlessElements(FormData* output) { if (render_frame() == nullptr || render_frame()->GetWebFrame() == nullptr) return false; @@ -785,8 +778,7 @@ void AutofillAgent::QueryAutofillSuggestions( 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, + GetDataListSuggestions(*input_element, &data_list_values, &data_list_labels); TrimStringVectorForIPC(&data_list_values); TrimStringVectorForIPC(&data_list_labels); @@ -797,7 +789,7 @@ void AutofillAgent::QueryAutofillSuggestions( GetAutofillDriver()->SetDataList(data_list_values, data_list_labels); GetAutofillDriver()->QueryFormFieldAutofill( autofill_query_id_, form, field, - render_frame()->GetRenderView()->ElementBoundsInWindow(element_), + render_frame()->ElementBoundsInWindow(element_), autoselect_first_suggestion); } @@ -821,7 +813,7 @@ void AutofillAgent::DoPreviewFieldWithValue(const base::string16& value, void AutofillAgent::ProcessForms() { // Record timestamp of when the forms are first seen. This is used to // measure the overhead of the Autofill feature. - base::TimeTicks forms_seen_timestamp = base::TimeTicks::Now(); + base::TimeTicks forms_seen_timestamp = AutofillTickClock::NowTicks(); WebLocalFrame* frame = render_frame()->GetWebFrame(); std::vector<FormData> forms = form_cache_.ExtractNewForms(); @@ -845,11 +837,6 @@ void AutofillAgent::HidePopup() { GetAutofillDriver()->HidePopup(); } -bool AutofillAgent::IsUserGesture() const { - return WebUserGestureIndicator::IsProcessingUserGesture( - render_frame()->GetWebFrame()); -} - void AutofillAgent::DidAssociateFormControlsDynamically() { // If the control flow is here than the document was at least loaded. The // whole page doesn't have to be loaded. @@ -883,8 +870,10 @@ void AutofillAgent::DidReceiveLeftMouseDownOrGestureTapInNode( DCHECK(!node.IsNull()); focused_node_was_last_clicked_ = node.Focused(); - if (IsKeyboardAccessoryEnabled() || !focus_requires_scroll_) + if (IsTouchToFillEnabled() || IsKeyboardAccessoryEnabled() || + !focus_requires_scroll_) { HandleFocusChangeComplete(); + } } void AutofillAgent::SelectControlDidChange( @@ -914,12 +903,9 @@ void AutofillAgent::SelectFieldOptionsChanged( bool AutofillAgent::ShouldSuppressKeyboard( const WebFormControlElement& element) { - // The keyboard should be suppressed if we can show the Touch To Fill UI. - // // Note: This is currently only implemented for passwords. Consider supporting // other autofill types in the future as well. - return IsTouchToFillEnabled() && - password_autofill_agent_->TryToShowTouchToFill(element); + return password_autofill_agent_->ShouldSuppressKeyboard(); } void AutofillAgent::SelectWasUpdated( @@ -946,6 +932,9 @@ void AutofillAgent::FormControlElementClicked( if (!input_element && !form_util::IsTextAreaElement(element)) return; + if (IsTouchToFillEnabled()) + password_autofill_agent_->TryToShowTouchToFill(element); + ShowSuggestionsOptions options; options.autofill_on_empty_values = true; // Show full suggestions when clicking on an already-focused form field. @@ -1023,8 +1012,7 @@ void AutofillAgent::OnProvisionallySaveForm( if (form_util::FindFormAndFieldForFormControlElement(element, &form, &field)) { GetAutofillDriver()->SelectControlDidChange( - form, field, - render_frame()->GetRenderView()->ElementBoundsInWindow(element)); + form, field, render_frame()->ElementBoundsInWindow(element)); } } } diff --git a/chromium/components/autofill/content/renderer/autofill_agent.h b/chromium/components/autofill/content/renderer/autofill_agent.h index e552ea1c5a6..0ea12f07a06 100644 --- a/chromium/components/autofill/content/renderer/autofill_agent.h +++ b/chromium/components/autofill/content/renderer/autofill_agent.h @@ -40,8 +40,6 @@ class WebVector; namespace autofill { struct FormData; -struct FormFieldData; -struct PasswordFormFillData; class PasswordAutofillAgent; class PasswordGenerationAgent; @@ -83,14 +81,12 @@ class AutofillAgent : public content::RenderFrameObserver, void ClearPreviewedForm() override; void FillFieldWithValue(const base::string16& value) override; void PreviewFieldWithValue(const base::string16& value) override; - void SetSuggestionAvailability(bool available) override; + void SetSuggestionAvailability(const mojom::AutofillState state) override; void AcceptDataListSuggestion(const base::string16& value) override; void FillPasswordSuggestion(const base::string16& username, const base::string16& password) override; void PreviewPasswordSuggestion(const base::string16& username, const base::string16& password) override; - void ShowInitialPasswordAccountSuggestions( - const PasswordFormFillData& form_data) override; void SetUserGestureRequired(bool required) override; void SetSecureContextRequired(bool required) override; void SetFocusRequiresScroll(bool require) override; @@ -232,12 +228,6 @@ class AutofillAgent : public content::RenderFrameObserver, // Sets the element value to reflect the selected |suggested_value|. void DoAcceptDataListSuggestion(const base::string16& suggested_value); - // Fills |form| and |field| with the FormData and FormField corresponding to - // |node|. Returns true if the data was found; and false otherwise. - bool FindFormAndFieldForNode(const blink::WebNode& node, - FormData* form, - FormFieldData* field) WARN_UNUSED_RESULT; - // Set |node| to display the given |value|. void DoFillFieldWithValue(const base::string16& value, blink::WebInputElement* node); @@ -251,24 +241,9 @@ class AutofillAgent : public content::RenderFrameObserver, // Notifies browser of new fillable forms in |render_frame|. void ProcessForms(); - // Sends a message to the browser that the form is about to be submitted, - // only if the particular message has not been previously submitted for the - // form in the current frame. - // Additionally, depending on |send_submitted_event| a message is sent to the - // browser that the form was submitted. - void SendFormEvents(const blink::WebFormElement& form, - bool send_submitted_event); - // Hides any currently showing Autofill popup. void HidePopup(); - // TODO(crbug.com/785524): Investigate why this method need to be mocked in - // chrome_render_view_test.cc, this isn't called now, but this is no test - // failed. - // Returns true if the text field change is due to a user gesture. Can be - // overriden in tests. - virtual bool IsUserGesture() const; - // Attempt to get submitted FormData from last_interacted_form_ or // provisionally_saved_form_, return true if |form| is set. bool GetSubmittedForm(FormData* form); diff --git a/chromium/components/autofill/content/renderer/form_autofill_util.cc b/chromium/components/autofill/content/renderer/form_autofill_util.cc index c33dba2fb0d..3ded21383a4 100644 --- a/chromium/components/autofill/content/renderer/form_autofill_util.cc +++ b/chromium/components/autofill/content/renderer/form_autofill_util.cc @@ -1486,57 +1486,22 @@ bool ExtractFormData(const WebFormElement& form_element, FormData* data) { data, nullptr); } -bool IsFormVisible(blink::WebLocalFrame* frame, - const blink::WebFormElement& form_element, - const GURL& canonical_action, - const GURL& canonical_origin, - const FormData& form_data) { - const GURL frame_origin = GetCanonicalOriginForDocument(frame->GetDocument()); - blink::WebVector<WebFormElement> forms; - frame->GetDocument().Forms(forms); - - // Omitting the action attribute would result in |canonical_origin| for - // hierarchical schemes like http:, and in an empty URL for non-hierarchical - // schemes like about: or data: etc. - const bool action_is_empty = canonical_action.is_empty() - || canonical_action == canonical_origin; - - // Since empty or unspecified action fields are automatically set to page URL, - // action field for forms cannot be used for comparing (all forms with - // empty/unspecified actions have the same value). If an action field is set - // to the page URL, this method checks ALL fields of the form instead (using - // FormData.SameFormAs). This is also true if the action was set to the page - // URL on purpose. - for (const WebFormElement& form : forms) { - if (!AreFormContentsVisible(form)) - continue; - - // Try to match the WebFormElement reference first. - if (!form_element.IsNull() && form == form_element) { - return true; // Form still exists. - } - - GURL iter_canonical_action = GetCanonicalActionForForm(form); - bool form_action_is_empty = iter_canonical_action.is_empty() || - iter_canonical_action == frame_origin; - if (action_is_empty != form_action_is_empty) - continue; - - if (action_is_empty) { // Both actions are empty, compare all fields. - FormData extracted_form_data; - WebFormElementToFormData(form, WebFormControlElement(), nullptr, - EXTRACT_NONE, &extracted_form_data, nullptr); - if (form_data.SameFormAs(extracted_form_data)) { - return true; // Form still exists. - } - } else { // Both actions are non-empty, compare actions only. - if (canonical_action == iter_canonical_action) { - return true; // Form still exists. - } - } - } +bool IsFormVisible(blink::WebLocalFrame* frame, uint32_t form_renderer_id) { + WebDocument doc = frame->GetDocument(); + if (doc.IsNull()) + return false; + WebFormElement form = FindFormByUniqueRendererId(doc, form_renderer_id); + return form.IsNull() ? false : AreFormContentsVisible(form); +} - return false; +bool IsFormControlVisible(blink::WebLocalFrame* frame, + uint32_t field_renderer_id) { + WebDocument doc = frame->GetDocument(); + if (doc.IsNull()) + return false; + WebFormControlElement field = + FindFormControlElementsByUniqueRendererId(doc, field_renderer_id); + return field.IsNull() ? false : IsWebElementVisible(field); } bool IsSomeControlElementVisible( @@ -1803,6 +1768,8 @@ bool WebFormElementToFormData( form->unique_renderer_id = form_element.UniqueRendererFormId(); form->url = GetCanonicalOriginForDocument(frame->GetDocument()); 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"); @@ -2015,23 +1982,6 @@ void FillForm(const FormData& form, const WebFormControlElement& element) { &FillFormField); } -void FillFormIncludingNonFocusableElements(const FormData& form_data, - const WebFormElement& form_element) { - if (form_element.IsNull()) { - NOTREACHED(); - return; - } - - FieldFilterMask filter_mask = static_cast<FieldFilterMask>( - FILTER_DISABLED_ELEMENTS | FILTER_READONLY_ELEMENTS); - ForEachMatchingFormField(form_element, - WebInputElement(), - form_data, - filter_mask, - true, /* force override */ - &FillFormField); -} - void PreviewForm(const FormData& form, const WebFormControlElement& element) { WebFormElement form_element = element.Form(); if (form_element.IsNull()) { diff --git a/chromium/components/autofill/content/renderer/form_autofill_util.h b/chromium/components/autofill/content/renderer/form_autofill_util.h index bb64fb7fe73..ac542f3d570 100644 --- a/chromium/components/autofill/content/renderer/form_autofill_util.h +++ b/chromium/components/autofill/content/renderer/form_autofill_util.h @@ -10,10 +10,13 @@ #include <set> #include <vector> +#include "base/i18n/rtl.h" #include "base/macros.h" #include "base/strings/string16.h" #include "components/autofill/core/common/autofill_constants.h" -#include "components/autofill/core/common/password_form_field_prediction_map.h" +#include "components/autofill/core/common/form_data.h" +#include "components/autofill/core/common/form_field_data.h" +#include "components/autofill/core/common/mojom/autofill_types.mojom-shared.h" #include "third_party/blink/public/platform/web_vector.h" #include "third_party/blink/public/web/web_element_collection.h" #include "ui/gfx/geometry/rect_f.h" @@ -71,15 +74,14 @@ GURL StripAuthAndParams(const GURL& gurl); // successful. bool ExtractFormData(const blink::WebFormElement& form_element, FormData* data); -// Helper function to check if there exist any visible form on |frame| which -// equals |form_element|. If |form_element| is null, checks if forms action -// equals |action|. Returns true if so. For forms with empty or unspecified -// actions, all form data are used for comparison. -bool IsFormVisible(blink::WebLocalFrame* frame, - const blink::WebFormElement& form_element, - const GURL& action, - const GURL& origin, - const FormData& form_data); +// Helper function to check if a form with renderer id |form_renderer_id| exists +// in |frame| and is visible. +bool IsFormVisible(blink::WebLocalFrame* frame, uint32_t form_renderer_id); + +// Helper function to check if a field with renderer id |field_renderer_id| +// exists in |frame| and is visible. +bool IsFormControlVisible(blink::WebLocalFrame* frame, + uint32_t field_renderer_id); // Returns true if at least one element from |control_elements| is visible. bool IsSomeControlElementVisible( @@ -220,12 +222,6 @@ bool FindFormAndFieldForFormControlElement( void FillForm(const FormData& form, const blink::WebFormControlElement& element); -// Fills focusable and non-focusable form control elements within |form_element| -// with field data from |form_data|. -void FillFormIncludingNonFocusableElements( - const FormData& form_data, - const blink::WebFormElement& form_element); - // Previews the form represented by |form|. |element| is the input element that // initiated the preview process. void PreviewForm(const FormData& form, 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 18df8232567..433bfc80b0a 100644 --- a/chromium/components/autofill/content/renderer/form_autofill_util_browsertest.cc +++ b/chromium/components/autofill/content/renderer/form_autofill_util_browsertest.cc @@ -7,6 +7,7 @@ #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" +#include "components/autofill/core/common/mojom/autofill_types.mojom-shared.h" #include "content/public/test/render_view_test.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/platform/web_string.h" @@ -20,18 +21,23 @@ #include "third_party/blink/public/web/web_local_frame.h" #include "third_party/blink/public/web/web_select_element.h" -using autofill::FormFieldData; using autofill::mojom::ButtonTitleType; using blink::WebDocument; using blink::WebElement; +using blink::WebElementCollection; using blink::WebFormControlElement; using blink::WebFormElement; using blink::WebInputElement; using blink::WebLocalFrame; +using blink::WebNode; using blink::WebSelectElement; using blink::WebString; using blink::WebVector; +namespace autofill { +namespace form_util { +namespace { + struct AutofillFieldLabelSourceCase { const char* html; const FormFieldData::LabelSource label_source; @@ -159,7 +165,7 @@ TEST_F(FormAutofillUtilsTest, FindChildTextTest) { WebElement target = web_frame->GetDocument().GetElementById("target"); ASSERT_FALSE(target.IsNull()); EXPECT_EQ(base::UTF8ToUTF16(test_case.expected_label), - autofill::form_util::FindChildText(target)); + FindChildText(target)); } } @@ -176,14 +182,13 @@ TEST_F(FormAutofillUtilsTest, FindChildTextSkipElementTest) { ASSERT_FALSE(target.IsNull()); WebVector<WebElement> web_to_skip = web_frame->GetDocument().QuerySelectorAll("div[class='skip']"); - std::set<blink::WebNode> to_skip; + std::set<WebNode> to_skip; for (size_t i = 0; i < web_to_skip.size(); ++i) { to_skip.insert(web_to_skip[i]); } EXPECT_EQ(base::UTF8ToUTF16(test_case.expected_label), - autofill::form_util::FindChildTextWithIgnoreListForTesting( - target, to_skip)); + FindChildTextWithIgnoreListForTesting(target, to_skip)); } } @@ -212,8 +217,8 @@ TEST_F(FormAutofillUtilsTest, InferLabelForElementTest) { FormFieldData::LabelSource label_source = FormFieldData::LabelSource::kUnknown; base::string16 label; - autofill::form_util::InferLabelForElementForTesting(form_target, stop_words, - &label, &label_source); + InferLabelForElementForTesting(form_target, stop_words, &label, + &label_source); EXPECT_EQ(base::UTF8ToUTF16(test_case.expected_label), label); } } @@ -289,8 +294,7 @@ TEST_F(FormAutofillUtilsTest, InferButtonTitleForFormTest) { const WebFormElement& form_target = target.ToConst<WebFormElement>(); ASSERT_FALSE(form_target.IsNull()); - autofill::ButtonTitleList actual = - autofill::form_util::InferButtonTitlesForTesting(form_target); + autofill::ButtonTitleList actual = InferButtonTitlesForTesting(form_target); autofill::ButtonTitleList expected = { {base::UTF8ToUTF16("Clear field"), ButtonTitleType::INPUT_ELEMENT_BUTTON_TYPE}, @@ -326,8 +330,7 @@ TEST_F(FormAutofillUtilsTest, InferButtonTitleForFormTest_TooLongTitle) { const WebFormElement& form_target = target.ToConst<WebFormElement>(); ASSERT_FALSE(form_target.IsNull()); - autofill::ButtonTitleList actual = - autofill::form_util::InferButtonTitlesForTesting(form_target); + autofill::ButtonTitleList actual = InferButtonTitlesForTesting(form_target); int total_length = 0; for (auto title : actual) { @@ -357,8 +360,7 @@ TEST_F(FormAutofillUtilsTest, InferButtonTitle_Formless) { const WebElement& body = web_frame->GetDocument().Body(); ASSERT_FALSE(body.IsNull()); - autofill::ButtonTitleList actual = - autofill::form_util::InferButtonTitlesForTesting(body); + autofill::ButtonTitleList actual = InferButtonTitlesForTesting(body); autofill::ButtonTitleList expected = { {base::UTF8ToUTF16("Show password"), ButtonTitleType::INPUT_ELEMENT_BUTTON_TYPE}, @@ -376,23 +378,22 @@ TEST_F(FormAutofillUtilsTest, IsEnabled) { "<input type='password' id='name3'>" "<input type='text' id='name4' disabled>"); - const std::vector<blink::WebElement> dummy_fieldsets; + const std::vector<WebElement> dummy_fieldsets; WebLocalFrame* web_frame = GetMainFrame(); ASSERT_TRUE(web_frame); - std::vector<blink::WebFormControlElement> control_elements; - blink::WebElementCollection inputs = + std::vector<WebFormControlElement> control_elements; + WebElementCollection inputs = web_frame->GetDocument().GetElementsByHTMLTagName("input"); - for (blink::WebElement element = inputs.FirstItem(); !element.IsNull(); + for (WebElement element = inputs.FirstItem(); !element.IsNull(); element = inputs.NextItem()) { - control_elements.push_back(element.To<blink::WebFormControlElement>()); + control_elements.push_back(element.To<WebFormControlElement>()); } autofill::FormData target; - EXPECT_TRUE( - autofill::form_util::UnownedPasswordFormElementsAndFieldSetsToFormData( - dummy_fieldsets, control_elements, nullptr, web_frame->GetDocument(), - nullptr, autofill::form_util::EXTRACT_NONE, &target, nullptr)); + EXPECT_TRUE(UnownedPasswordFormElementsAndFieldSetsToFormData( + dummy_fieldsets, control_elements, nullptr, web_frame->GetDocument(), + nullptr, EXTRACT_NONE, &target, nullptr)); const struct { const char* const name; bool enabled; @@ -415,23 +416,22 @@ TEST_F(FormAutofillUtilsTest, IsReadonly) { "<input type='password' id='name3'>" "<input type='text' id='name4' readonly>"); - const std::vector<blink::WebElement> dummy_fieldsets; + const std::vector<WebElement> dummy_fieldsets; WebLocalFrame* web_frame = GetMainFrame(); ASSERT_TRUE(web_frame); - std::vector<blink::WebFormControlElement> control_elements; - blink::WebElementCollection inputs = + std::vector<WebFormControlElement> control_elements; + WebElementCollection inputs = web_frame->GetDocument().GetElementsByHTMLTagName("input"); - for (blink::WebElement element = inputs.FirstItem(); !element.IsNull(); + for (WebElement element = inputs.FirstItem(); !element.IsNull(); element = inputs.NextItem()) { - control_elements.push_back(element.To<blink::WebFormControlElement>()); + control_elements.push_back(element.To<WebFormControlElement>()); } autofill::FormData target; - EXPECT_TRUE( - autofill::form_util::UnownedPasswordFormElementsAndFieldSetsToFormData( - dummy_fieldsets, control_elements, nullptr, web_frame->GetDocument(), - nullptr, autofill::form_util::EXTRACT_NONE, &target, nullptr)); + EXPECT_TRUE(UnownedPasswordFormElementsAndFieldSetsToFormData( + dummy_fieldsets, control_elements, nullptr, web_frame->GetDocument(), + nullptr, EXTRACT_NONE, &target, nullptr)); const struct { const char* const name; bool readonly; @@ -452,27 +452,26 @@ TEST_F(FormAutofillUtilsTest, IsFocusable) { "<input type='text' id='name1' value='123'>" "<input type='text' id='name2' style='display:none'>"); - const std::vector<blink::WebElement> dummy_fieldsets; + const std::vector<WebElement> dummy_fieldsets; WebLocalFrame* web_frame = GetMainFrame(); ASSERT_TRUE(web_frame); - std::vector<blink::WebFormControlElement> control_elements; + std::vector<WebFormControlElement> control_elements; control_elements.push_back(web_frame->GetDocument() .GetElementById("name1") - .To<blink::WebFormControlElement>()); + .To<WebFormControlElement>()); control_elements.push_back(web_frame->GetDocument() .GetElementById("name2") - .To<blink::WebFormControlElement>()); + .To<WebFormControlElement>()); EXPECT_TRUE(autofill::form_util::IsWebElementVisible(control_elements[0])); EXPECT_FALSE(autofill::form_util::IsWebElementVisible(control_elements[1])); autofill::FormData target; - EXPECT_TRUE( - autofill::form_util::UnownedPasswordFormElementsAndFieldSetsToFormData( - dummy_fieldsets, control_elements, nullptr, web_frame->GetDocument(), - nullptr, autofill::form_util::EXTRACT_NONE, &target, nullptr)); + EXPECT_TRUE(UnownedPasswordFormElementsAndFieldSetsToFormData( + dummy_fieldsets, control_elements, nullptr, web_frame->GetDocument(), + nullptr, EXTRACT_NONE, &target, nullptr)); ASSERT_EQ(2u, target.fields.size()); EXPECT_EQ(base::UTF8ToUTF16("name1"), target.fields[0].name); EXPECT_TRUE(target.fields[0].is_focusable); @@ -483,19 +482,17 @@ TEST_F(FormAutofillUtilsTest, IsFocusable) { TEST_F(FormAutofillUtilsTest, FindFormByUniqueId) { LoadHTML("<body><form id='form1'></form><form id='form2'></form></body>"); WebDocument doc = GetMainFrame()->GetDocument(); - blink::WebVector<WebFormElement> forms; + WebVector<WebFormElement> forms; doc.Forms(forms); for (const auto& form : forms) { - EXPECT_EQ(form, autofill::form_util::FindFormByUniqueRendererId( - doc, form.UniqueRendererFormId())); + EXPECT_EQ(form, + FindFormByUniqueRendererId(doc, form.UniqueRendererFormId())); } // Expect null form element for non-existing form id. uint32_t non_existing_id = forms[0].UniqueRendererFormId() + 1000; - EXPECT_TRUE( - autofill::form_util::FindFormByUniqueRendererId(doc, non_existing_id) - .IsNull()); + EXPECT_TRUE(FindFormByUniqueRendererId(doc, non_existing_id).IsNull()); } TEST_F(FormAutofillUtilsTest, FindFormControlByUniqueId) { @@ -505,7 +502,6 @@ TEST_F(FormAutofillUtilsTest, FindFormControlByUniqueId) { auto input1 = doc.GetElementById("i1").To<WebInputElement>(); auto input2 = doc.GetElementById("i2").To<WebInputElement>(); uint32_t non_existing_id = input2.UniqueRendererFormControlId() + 1000; - using autofill::form_util::FindFormControlElementsByUniqueRendererId; EXPECT_EQ(input1, FindFormControlElementsByUniqueRendererId( doc, input1.UniqueRendererFormControlId())); @@ -526,9 +522,7 @@ TEST_F(FormAutofillUtilsTest, FindFormControlElementsByUniqueIdNoForm) { non_existing_id, input1.UniqueRendererFormControlId()}; - auto elements = - autofill::form_util::FindFormControlElementsByUniqueRendererId( - doc, renderer_ids); + auto elements = FindFormControlElementsByUniqueRendererId(doc, renderer_ids); ASSERT_EQ(3u, elements.size()); EXPECT_EQ(input3, elements[0]); @@ -550,9 +544,8 @@ TEST_F(FormAutofillUtilsTest, FindFormControlElementsByUniqueIdWithForm) { non_existing_id, input1.UniqueRendererFormControlId()}; - auto elements = - autofill::form_util::FindFormControlElementsByUniqueRendererId( - doc, form.UniqueRendererFormId(), renderer_ids); + auto elements = FindFormControlElementsByUniqueRendererId( + doc, form.UniqueRendererFormId(), renderer_ids); // |input3| is not in the form, so it shouldn't be returned. ASSERT_EQ(3u, elements.size()); @@ -562,7 +555,7 @@ TEST_F(FormAutofillUtilsTest, FindFormControlElementsByUniqueIdWithForm) { // Expect that no elements are retured for non existing form id. uint32_t non_existing_form_id = form.UniqueRendererFormId() + 1000; - elements = autofill::form_util::FindFormControlElementsByUniqueRendererId( + elements = FindFormControlElementsByUniqueRendererId( doc, non_existing_form_id, renderer_ids); ASSERT_EQ(3u, elements.size()); EXPECT_TRUE(elements[0].IsNull()); @@ -692,3 +685,61 @@ TEST_F(FormAutofillUtilsTest, GetAriaDescribedByInvalid) { EXPECT_EQ(autofill::form_util::GetAriaDescription(doc, element), base::UTF8ToUTF16("")); } + +TEST_F(FormAutofillUtilsTest, IsFormVisible) { + LoadHTML("<body><form id='form1'><input id='i1'></form></body>"); + WebDocument doc = GetMainFrame()->GetDocument(); + auto form = doc.GetElementById("form1").To<WebFormElement>(); + uint32_t form_id = form.UniqueRendererFormId(); + + EXPECT_TRUE(autofill::form_util::IsFormVisible(GetMainFrame(), form_id)); + + // Hide a form. + form.SetAttribute("style", "display:none"); + EXPECT_FALSE(autofill::form_util::IsFormVisible(GetMainFrame(), form_id)); +} + +TEST_F(FormAutofillUtilsTest, IsFormControlVisible) { + LoadHTML("<body><input id='input1'></body>"); + WebDocument doc = GetMainFrame()->GetDocument(); + auto input = doc.GetElementById("input1").To<WebFormControlElement>(); + uint32_t input_id = input.UniqueRendererFormControlId(); + + EXPECT_TRUE(IsFormControlVisible(GetMainFrame(), input_id)); + + // Hide a field. + input.SetAttribute("style", "display:none"); + EXPECT_FALSE(autofill::form_util::IsFormVisible(GetMainFrame(), input_id)); +} + +TEST_F(FormAutofillUtilsTest, IsActionEmptyFalse) { + LoadHTML( + "<body><form id='form1' action='done.html'><input " + "id='i1'></form></body>"); + WebDocument doc = GetMainFrame()->GetDocument(); + auto web_form = doc.GetElementById("form1").To<WebFormElement>(); + + FormData form_data; + ASSERT_TRUE(WebFormElementToFormData( + web_form, WebFormControlElement(), nullptr /*field_data_manager*/, + EXTRACT_VALUE, &form_data, nullptr /* FormFieldData */)); + + EXPECT_FALSE(form_data.is_action_empty); +} + +TEST_F(FormAutofillUtilsTest, IsActionEmptyTrue) { + LoadHTML("<body><form id='form1'><input id='i1'></form></body>"); + WebDocument doc = GetMainFrame()->GetDocument(); + auto web_form = doc.GetElementById("form1").To<WebFormElement>(); + + FormData form_data; + ASSERT_TRUE(WebFormElementToFormData( + web_form, WebFormControlElement(), nullptr /*field_data_manager*/, + EXTRACT_VALUE, &form_data, nullptr /* FormFieldData */)); + + EXPECT_TRUE(form_data.is_action_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 0e67646e8b7..cb6edb14c4a 100644 --- a/chromium/components/autofill/content/renderer/form_cache.cc +++ b/chromium/components/autofill/content/renderer/form_cache.cc @@ -5,6 +5,7 @@ #include "components/autofill/content/renderer/form_cache.h" #include <algorithm> +#include <set> #include <string> #include <utility> @@ -12,6 +13,7 @@ #include "base/logging.h" #include "base/macros.h" #include "base/stl_util.h" +#include "base/strings/strcat.h" #include "base/strings/string_piece.h" #include "base/strings/string_split.h" #include "base/strings/stringprintf.h" @@ -161,15 +163,16 @@ void LogDeprecationMessages(const WebFormControlElement& element) { for (const char* str : deprecated) { if (autocomplete_attribute.find(str) == std::string::npos) continue; - std::string msg = std::string("autocomplete='") + str + - "' is deprecated and will soon be ignored. See http://goo.gl/YjeSsW"; + std::string msg = base::StrCat( + {"autocomplete='", str, + "' is deprecated and will soon be ignored. See http://goo.gl/YjeSsW"}); WebConsoleMessage console_message = WebConsoleMessage( blink::mojom::ConsoleMessageLevel::kWarning, WebString::FromASCII(msg)); element.GetDocument().GetFrame()->AddMessageToConsole(console_message); } } -// Determines whether the form is interesting enough to send to the browser +// Determines whether the form is interesting enough to be sent to the browser // for further operations. bool IsFormInteresting(const FormData& form, size_t num_editable_elements) { if (form.fields.empty()) @@ -199,9 +202,7 @@ bool IsFormInteresting(const FormData& form, size_t num_editable_elements) { } // namespace FormCache::FormCache(WebLocalFrame* frame) : frame_(frame) {} - -FormCache::~FormCache() { -} +FormCache::~FormCache() = default; std::vector<FormData> FormCache::ExtractNewForms() { std::vector<FormData> forms; @@ -214,6 +215,8 @@ std::vector<FormData> FormCache::ExtractNewForms() { WebVector<WebFormElement> web_forms; document.Forms(web_forms); + std::set<uint32_t> observed_unique_renderer_ids; + // Log an error message for deprecated attributes, but only the first time // the form is parsed. bool log_deprecation_messages = parsed_forms_.empty(); @@ -223,14 +226,12 @@ std::vector<FormData> FormCache::ExtractNewForms() { form_util::EXTRACT_OPTIONS); size_t num_fields_seen = 0; - for (size_t i = 0; i < web_forms.size(); ++i) { - const WebFormElement& form_element = web_forms[i]; - + for (const WebFormElement& form_element : web_forms) { std::vector<WebFormControlElement> control_elements = form_util::ExtractAutofillableElementsInForm(form_element); + size_t num_editable_elements = ScanFormControlElements(control_elements, log_deprecation_messages); - if (num_editable_elements == 0) continue; @@ -240,9 +241,14 @@ std::vector<FormData> FormCache::ExtractNewForms() { continue; } + for (const auto& field : form.fields) + observed_unique_renderer_ids.insert(field.unique_renderer_id); + num_fields_seen += form.fields.size(); - if (num_fields_seen > form_util::kMaxParseableFields) + if (num_fields_seen > form_util::kMaxParseableFields) { + PruneInitialValueCaches(observed_unique_renderer_ids); return forms; + } if (!base::Contains(parsed_forms_, form) && IsFormInteresting(form, num_editable_elements)) { @@ -267,22 +273,29 @@ std::vector<FormData> FormCache::ExtractNewForms() { size_t num_editable_elements = ScanFormControlElements(control_elements, log_deprecation_messages); - - if (num_editable_elements == 0) + if (num_editable_elements == 0) { + PruneInitialValueCaches(observed_unique_renderer_ids); return forms; + } FormData synthetic_form; if (!UnownedCheckoutFormElementsAndFieldSetsToFormData( fieldsets, control_elements, nullptr, document, extract_mask, &synthetic_form, nullptr)) { + PruneInitialValueCaches(observed_unique_renderer_ids); return forms; } + for (const auto& field : synthetic_form.fields) + observed_unique_renderer_ids.insert(field.unique_renderer_id); + num_fields_seen += synthetic_form.fields.size(); - if (num_fields_seen > form_util::kMaxParseableFields) + if (num_fields_seen > form_util::kMaxParseableFields) { + PruneInitialValueCaches(observed_unique_renderer_ids); return forms; + } - if (!parsed_forms_.count(synthetic_form) && + if (!base::Contains(parsed_forms_, synthetic_form) && IsFormInteresting(synthetic_form, num_editable_elements)) { SaveInitialValues(control_elements); forms.push_back(synthetic_form); @@ -290,6 +303,8 @@ std::vector<FormData> FormCache::ExtractNewForms() { parsed_forms_.erase(synthetic_form_); synthetic_form_ = synthetic_form; } + + PruneInitialValueCaches(observed_unique_renderer_ids); return forms; } @@ -302,16 +317,13 @@ void FormCache::Reset() { bool FormCache::ClearSectionWithElement(const WebFormControlElement& element) { WebFormElement form_element = element.Form(); - std::vector<WebFormControlElement> control_elements; - if (form_element.IsNull()) { - control_elements = form_util::GetUnownedAutofillableFormFieldElements( - element.GetDocument().All(), nullptr); - } else { - control_elements = - form_util::ExtractAutofillableElementsInForm(form_element); - } - for (size_t i = 0; i < control_elements.size(); ++i) { - WebFormControlElement control_element = control_elements[i]; + std::vector<WebFormControlElement> control_elements = + form_element.IsNull() + ? form_util::GetUnownedAutofillableFormFieldElements( + element.GetDocument().All(), nullptr) + : form_util::ExtractAutofillableElementsInForm(form_element); + + for (WebFormControlElement& control_element : control_elements) { // Don't modify the value of disabled fields. if (!control_element.IsEnabled()) continue; @@ -341,8 +353,8 @@ bool FormCache::ClearSectionWithElement(const WebFormControlElement& element) { } else if (form_util::IsSelectElement(control_element)) { WebSelectElement select_element = control_element.To<WebSelectElement>(); - std::map<const WebSelectElement, base::string16>::const_iterator - initial_value_iter = initial_select_values_.find(select_element); + auto initial_value_iter = initial_select_values_.find( + select_element.UniqueRendererFormControlId()); if (initial_value_iter != initial_select_values_.end() && select_element.Value().Utf16() != initial_value_iter->second) { select_element.SetAutofillValue( @@ -351,11 +363,11 @@ bool FormCache::ClearSectionWithElement(const WebFormControlElement& element) { } else { WebInputElement input_element = control_element.To<WebInputElement>(); DCHECK(form_util::IsCheckableElement(&input_element)); - std::map<const WebInputElement, bool>::const_iterator it = - initial_checked_state_.find(input_element); - if (it != initial_checked_state_.end() && - input_element.IsChecked() != it->second) { - input_element.SetChecked(it->second, true); + auto checkable_element_it = initial_checked_state_.find( + input_element.UniqueRendererFormControlId()); + if (checkable_element_it != initial_checked_state_.end() && + input_element.IsChecked() != checkable_element_it->second) { + input_element.SetChecked(checkable_element_it->second, true); } } } @@ -381,12 +393,10 @@ bool FormCache::ShowPredictions(const FormDataPredictions& form, if (!found_synthetic_form) { // Find the real form by searching through the WebDocuments. bool found_form = false; - WebFormElement form_element; WebVector<WebFormElement> web_forms; frame_->GetDocument().Forms(web_forms); - for (size_t i = 0; i < web_forms.size(); ++i) { - form_element = web_forms[i]; + for (const WebFormElement& form_element : web_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| @@ -445,32 +455,15 @@ bool FormCache::ShowPredictions(const FormDataPredictions& form, const base::string16 truncated_label = field_data.label.substr( 0, std::min(field_data.label.length(), kMaxLabelSize)); - // A rough estimate of the maximum title size is: - // 8 field titles at <17 chars each - // + 7 values at <40 chars each - // + 1 truncated label at <kMaxLabelSize; - // = 516 chars, rounded up to the next multiple of 64 = 576 - // A particularly large parseable name could blow through this and cause - // another allocation, but that's OK. - constexpr size_t kMaxTitleSize = 576; - std::string title; - title.reserve(kMaxTitleSize); - title += "overall type: "; - title += field.overall_type; - title += "\nserver type: "; - title += field.server_type; - title += "\nheuristic type: "; - title += field.heuristic_type; - title += "\nlabel: "; - title += base::UTF16ToUTF8(truncated_label); - title += "\nparseable name: "; - title += field.parseable_name; - title += "\nsection: "; - title += field.section; - title += "\nfield signature: "; - title += field.signature; - title += "\nform signature: "; - title += form.signature; + std::string title = + base::StrCat({"overall type: ", field.overall_type, // + "\nserver type: ", field.server_type, // + "\nheuristic type: ", field.heuristic_type, // + "\nlabel: ", base::UTF16ToUTF8(truncated_label), // + "\nparseable name: ", field.parseable_name, // + "\nsection: ", field.section, // + "\nfield signature: ", field.signature, // + "\nform signature: ", form.signature}); // Set this debug string to the title so that a developer can easily debug // by hovering the mouse over the input field. @@ -494,9 +487,7 @@ size_t FormCache::ScanFormControlElements( const std::vector<WebFormControlElement>& control_elements, bool log_deprecation_messages) { size_t num_editable_elements = 0; - for (size_t i = 0; i < control_elements.size(); ++i) { - const WebFormControlElement& element = control_elements[i]; - + for (const WebFormControlElement& element : control_elements) { if (log_deprecation_messages) LogDeprecationMessages(element); @@ -521,12 +512,14 @@ void FormCache::SaveInitialValues( const WebSelectElement select_element = element.ToConst<WebSelectElement>(); initial_select_values_.insert( - std::make_pair(select_element, select_element.Value().Utf16())); + std::make_pair(select_element.UniqueRendererFormControlId(), + select_element.Value().Utf16())); } else { const WebInputElement* input_element = ToWebInputElement(&element); if (form_util::IsCheckableElement(input_element)) { initial_checked_state_.insert( - std::make_pair(*input_element, input_element->IsChecked())); + std::make_pair(input_element->UniqueRendererFormControlId(), + input_element->IsChecked())); } } } @@ -564,4 +557,25 @@ bool FormCache::ShouldShowAutocompleteConsoleWarnings( return false; } +void FormCache::PruneInitialValueCaches( + const std::set<uint32_t> ids_to_retain) { + // Prune initial_select_values_. + for (auto iter = initial_select_values_.begin(); + iter != initial_select_values_.end();) { + if (!base::Contains(ids_to_retain, iter->first)) + iter = initial_select_values_.erase(iter); + else + ++iter; + } + + // Prune initial_checked_state_. + for (auto iter = initial_checked_state_.begin(); + iter != initial_checked_state_.end();) { + if (!base::Contains(ids_to_retain, iter->first)) + iter = initial_checked_state_.erase(iter); + else + ++iter; + } +} + } // namespace autofill diff --git a/chromium/components/autofill/content/renderer/form_cache.h b/chromium/components/autofill/content/renderer/form_cache.h index c4db022d8cb..9cc7da62142 100644 --- a/chromium/components/autofill/content/renderer/form_cache.h +++ b/chromium/components/autofill/content/renderer/form_cache.h @@ -18,13 +18,12 @@ namespace blink { class WebFormControlElement; -class WebInputElement; class WebLocalFrame; -class WebSelectElement; } namespace autofill { +struct FormData; struct FormDataPredictions; // Manages the forms in a single RenderFrame. @@ -34,7 +33,8 @@ class FormCache { ~FormCache(); // Scans the DOM in |frame_| extracting and storing forms that have not been - // seen before. Returns the extracted forms. + // seen before. Returns the extracted forms. Note that modified forms are + // considered new forms. std::vector<FormData> ExtractNewForms(); // Resets the forms. @@ -57,10 +57,10 @@ class FormCache { ShouldShowAutocompleteConsoleWarnings_Enabled); FRIEND_TEST_ALL_PREFIXES(FormCacheTest, ShouldShowAutocompleteConsoleWarnings_Disabled); + FRIEND_TEST_ALL_PREFIXES(FormCacheBrowserTest, FreeDataOnElementRemoval); // Scans |control_elements| and returns the number of editable elements. - // Also remembers the initial <select> and <input> element states, and - // logs warning messages for deprecated attribute if + // Also logs warning messages for deprecated attribute if // |log_deprecation_messages| is set. size_t ScanFormControlElements( const std::vector<blink::WebFormControlElement>& control_elements, @@ -78,6 +78,10 @@ class FormCache { const std::string& predicted_autocomplete, const std::string& actual_autocomplete); + // Clears all entries from |initial_select_values_| and + // |initial_checked_state_| whose keys not contained in |ids_to_retain|. + void PruneInitialValueCaches(const std::set<uint32_t> ids_to_retain); + // The frame this FormCache is associated with. Weak reference. blink::WebLocalFrame* frame_; @@ -88,12 +92,13 @@ class FormCache { // form owner. FormData synthetic_form_; - // The cached initial values for <select> elements. - std::map<const blink::WebSelectElement, base::string16> - initial_select_values_; + // The cached initial values for <select> elements. Entries are keyed by + // unique_renderer_form_control_id of the WebSelectElements. + std::map<uint32_t, base::string16> initial_select_values_; - // The cached initial values for checkable <input> elements. - std::map<const blink::WebInputElement, bool> initial_checked_state_; + // The cached initial values for checkable <input> elements. Entries are + // keyed by the unique_renderer_form_control_id of the WebInputElements. + std::map<uint32_t, bool> initial_checked_state_; DISALLOW_COPY_AND_ASSIGN(FormCache); }; diff --git a/chromium/components/autofill/content/renderer/form_cache_browsertest.cc b/chromium/components/autofill/content/renderer/form_cache_browsertest.cc new file mode 100644 index 00000000000..f55fadb394e --- /dev/null +++ b/chromium/components/autofill/content/renderer/form_cache_browsertest.cc @@ -0,0 +1,194 @@ +// Copyright 2019 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 "components/autofill/content/renderer/form_cache.h" + +#include "base/strings/utf_string_conversions.h" +#include "components/autofill/content/renderer/form_autofill_util.h" +#include "components/autofill/core/common/form_field_data.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_input_element.h" +#include "third_party/blink/public/web/web_local_frame.h" +#include "third_party/blink/public/web/web_select_element.h" + +using base::ASCIIToUTF16; +using blink::WebDocument; +using blink::WebElement; +using blink::WebInputElement; +using blink::WebSelectElement; +using blink::WebString; + +namespace autofill { + +const FormData* GetFormByName(const std::vector<FormData>& forms, + base::StringPiece name) { + for (const FormData& form : forms) { + if (form.name == ASCIIToUTF16(name)) + return &form; + } + return nullptr; +} + +class FormCacheBrowserTest : public content::RenderViewTest { + public: + FormCacheBrowserTest() = default; + ~FormCacheBrowserTest() override = default; +}; + +TEST_F(FormCacheBrowserTest, ExtractForms) { + LoadHTML(R"( + <form id="form1"> + <input type="text" name="foo1"> + <input type="text" name="foo2"> + <input type="text" name="foo3"> + </form> + <input type="text" name="unowned_element"> + )"); + + FormCache form_cache(GetMainFrame()); + std::vector<FormData> forms = form_cache.ExtractNewForms(); + + const FormData* form1 = GetFormByName(forms, "form1"); + ASSERT_TRUE(form1); + EXPECT_EQ(3u, form1->fields.size()); + + const FormData* unowned_form = GetFormByName(forms, ""); + ASSERT_TRUE(unowned_form); + EXPECT_EQ(1u, unowned_form->fields.size()); +} + +TEST_F(FormCacheBrowserTest, ExtractFormsTwice) { + LoadHTML(R"( + <form id="form1"> + <input type="text" name="foo1"> + <input type="text" name="foo2"> + <input type="text" name="foo3"> + </form> + <input type="text" name="unowned_element"> + )"); + + FormCache form_cache(GetMainFrame()); + std::vector<FormData> forms = form_cache.ExtractNewForms(); + + forms = form_cache.ExtractNewForms(); + // As nothing has changed, there are no new forms and |forms| should be empty. + EXPECT_TRUE(forms.empty()); +} + +TEST_F(FormCacheBrowserTest, ExtractFormsAfterModification) { + LoadHTML(R"( + <form id="form1"> + <input type="text" name="foo1"> + <input type="text" name="foo2"> + <input type="text" name="foo3"> + </form> + <input type="text" name="unowned_element"> + )"); + + FormCache form_cache(GetMainFrame()); + std::vector<FormData> forms = form_cache.ExtractNewForms(); + + // Append an input element to the form and to the list of unowned inputs. + ExecuteJavaScriptForTests(R"( + var new_input_1 = document.createElement("input"); + new_input_1.setAttribute("type", "text"); + new_input_1.setAttribute("name", "foo4"); + + var form1 = document.getElementById("form1"); + form1.appendChild(new_input_1); + + var new_input_2 = document.createElement("input"); + new_input_2.setAttribute("type", "text"); + new_input_2.setAttribute("name", "unowned_element_2"); + document.body.appendChild(new_input_2); + )"); + + forms = form_cache.ExtractNewForms(); + + const FormData* form1 = GetFormByName(forms, "form1"); + ASSERT_TRUE(form1); + EXPECT_EQ(4u, form1->fields.size()); + + const FormData* unowned_form = GetFormByName(forms, ""); + ASSERT_TRUE(unowned_form); + EXPECT_EQ(2u, unowned_form->fields.size()); +} + +TEST_F(FormCacheBrowserTest, FillAndClear) { + LoadHTML(R"( + <input type="text" name="text" id="text"> + <input type="checkbox" checked name="checkbox" id="checkbox"> + <select name="select" id="select"> + <option value="first">first</option> + <option value="second" selected>second</option> + </select> + )"); + + FormCache form_cache(GetMainFrame()); + std::vector<FormData> forms = form_cache.ExtractNewForms(); + + ASSERT_EQ(1u, forms.size()); + FormData values_to_fill = forms[0]; + values_to_fill.fields[0].value = ASCIIToUTF16("test"); + values_to_fill.fields[0].is_autofilled = true; + values_to_fill.fields[1].check_status = + FormFieldData::CheckStatus::kCheckableButUnchecked; + values_to_fill.fields[1].is_autofilled = true; + values_to_fill.fields[2].value = ASCIIToUTF16("first"); + values_to_fill.fields[2].is_autofilled = true; + + WebDocument doc = GetMainFrame()->GetDocument(); + auto text = doc.GetElementById("text").To<WebInputElement>(); + auto checkbox = doc.GetElementById("checkbox").To<WebInputElement>(); + auto select_element = doc.GetElementById("select").To<WebSelectElement>(); + + form_util::FillForm(values_to_fill, text); + + EXPECT_EQ("test", text.Value().Ascii()); + EXPECT_FALSE(checkbox.IsChecked()); + EXPECT_EQ("first", select_element.Value().Ascii()); + + // Validate that clearing works, in particular that the previous values + // were saved correctly. + form_cache.ClearSectionWithElement(text); + + EXPECT_EQ("", text.Value().Ascii()); + EXPECT_TRUE(checkbox.IsChecked()); + EXPECT_EQ("second", select_element.Value().Ascii()); +} + +TEST_F(FormCacheBrowserTest, FreeDataOnElementRemoval) { + LoadHTML(R"( + <div id="container"> + <input type="text" name="text" id="text"> + <input type="checkbox" checked name="checkbox" id="checkbox"> + <select name="select" id="select"> + <option value="first">first</option> + <option value="second" selected>second</option> + </select> + </div> + )"); + + FormCache form_cache(GetMainFrame()); + form_cache.ExtractNewForms(); + + EXPECT_EQ(1u, form_cache.initial_select_values_.size()); + EXPECT_EQ(1u, form_cache.initial_checked_state_.size()); + + ExecuteJavaScriptForTests(R"( + const container = document.getElementById('container'); + while (container.childElementCount > 0) { + container.removeChild(container.children.item(0)); + } + )"); + + std::vector<FormData> forms = form_cache.ExtractNewForms(); + EXPECT_EQ(0u, forms.size()); + EXPECT_EQ(0u, form_cache.initial_select_values_.size()); + EXPECT_EQ(0u, form_cache.initial_checked_state_.size()); +} + +} // 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 2a5bc31ad6a..030c74d3ddb 100644 --- a/chromium/components/autofill/content/renderer/password_autofill_agent.cc +++ b/chromium/components/autofill/content/renderer/password_autofill_agent.cc @@ -14,6 +14,7 @@ #include "base/bind.h" #include "base/i18n/case_conversion.h" +#include "base/metrics/field_trial_params.h" #include "base/metrics/histogram_macros.h" #include "base/no_destructor.h" #include "base/numerics/safe_conversions.h" @@ -29,6 +30,7 @@ #include "components/autofill/content/renderer/prefilled_values_detector.h" #include "components/autofill/content/renderer/renderer_save_password_progress_logger.h" #include "components/autofill/core/common/autofill_constants.h" +#include "components/autofill/core/common/autofill_features.h" #include "components/autofill/core/common/autofill_util.h" #include "components/autofill/core/common/form_field_data.h" #include "components/autofill/core/common/mojom/autofill_types.mojom.h" @@ -72,6 +74,10 @@ using blink::WebView; namespace autofill { +using form_util::FindFormControlElementsByUniqueRendererId; +using form_util::IsFormControlVisible; +using form_util::IsFormVisible; + using mojom::FocusedFieldType; using mojom::SubmissionIndicatorEvent; using mojom::SubmissionSource; @@ -81,9 +87,6 @@ namespace { // The size above which we stop triggering autocomplete. const size_t kMaximumTextSizeForAutocomplete = 1000; -const char kDummyUsernameField[] = "anonymous_username"; -const char kDummyPasswordField[] = "anonymous_password"; - // Names of HTML attributes to show form and field signatures for debugging. const char kDebugAttributeForFormSignature[] = "form_signature"; const char kDebugAttributeForFieldSignature[] = "field_signature"; @@ -99,198 +102,6 @@ typedef SavePasswordProgressLogger Logger; typedef std::vector<FormInputElementMap> FormElementsList; -bool FillDataContainsFillableUsername(const PasswordFormFillData& fill_data) { - return !fill_data.username_field.name.empty() && - (!fill_data.additional_logins.empty() || - !fill_data.username_field.value.empty()); -} - -// Returns true if password form has username and password fields with either -// same or no name and id attributes supplied. -bool DoesFormContainAmbiguousOrEmptyNames( - const PasswordFormFillData& fill_data) { - return (fill_data.username_field.name == fill_data.password_field.name) || - (fill_data.password_field.name == - base::ASCIIToUTF16(kDummyPasswordField) && - (!FillDataContainsFillableUsername(fill_data) || - fill_data.username_field.name == - base::ASCIIToUTF16(kDummyUsernameField))); -} - -bool IsFieldPasswordField(const FormFieldData& field) { - return (field.form_control_type == "password"); -} - -// Returns true if any password field within |control_elements| is supplied with -// either |autocomplete='current-password'| or |autocomplete='new-password'| -// attribute. -bool HasPasswordWithAutocompleteAttribute( - const std::vector<WebFormControlElement>& control_elements) { - for (const WebFormControlElement& control_element : control_elements) { - if (!control_element.HasHTMLTagName("input")) - continue; - - const WebInputElement input_element = - control_element.ToConst<WebInputElement>(); - const AutocompleteFlag flag = AutocompleteFlagForElement(input_element); - if (input_element.IsPasswordFieldForAutofill() && - (flag == AutocompleteFlag::CURRENT_PASSWORD || - flag == AutocompleteFlag::NEW_PASSWORD)) { - return true; - } - } - - return false; -} - -// Returns the |field|'s autofillable name. If |ambiguous_or_empty_names| is set -// to true returns a dummy name instead. -base::string16 FieldName(const FormFieldData& field, - bool ambiguous_or_empty_names) { - return ambiguous_or_empty_names - ? (IsFieldPasswordField(field) - ? base::ASCIIToUTF16(kDummyPasswordField) - : base::ASCIIToUTF16(kDummyUsernameField)) - : field.name; -} - -bool IsUnownedPasswordFormVisible(const WebInputElement& input_element) { - return !input_element.IsNull() && - form_util::IsWebElementVisible(input_element); -} - -// Utility function to find the unique entry of |control_elements| for the -// specified input |field|. On successful find, adds it to |result| and returns -// |true|. Otherwise clears the references from each |HTMLInputElement| from -// |result| and returns |false|. -bool FindFormInputElement( - const std::vector<WebFormControlElement>& control_elements, - const FormFieldData& field, - bool ambiguous_or_empty_names, - FormInputElementMap* result) { - // Match the first input element, if any. - bool found_input = false; - bool is_password_field = IsFieldPasswordField(field); - bool does_password_field_has_ambigous_or_empty_name = - ambiguous_or_empty_names && is_password_field; - bool ambiguous_and_multiple_password_fields_with_autocomplete = - does_password_field_has_ambigous_or_empty_name && - HasPasswordWithAutocompleteAttribute(control_elements); - base::string16 field_name = FieldName(field, ambiguous_or_empty_names); - for (const WebFormControlElement& control_element : control_elements) { - if (!ambiguous_or_empty_names && - control_element.NameForAutofill().Utf16() != field_name) { - continue; - } - - if (!control_element.HasHTMLTagName("input")) - continue; - - // Only fill saved passwords into password fields and usernames into text - // fields. - const WebInputElement input_element = - control_element.ToConst<WebInputElement>(); - if (!input_element.IsTextField() || - input_element.IsPasswordFieldForAutofill() != is_password_field) - continue; - - // For change password form with ambiguous or empty names keep only the - // first password field having |autocomplete='current-password'| attribute - // set. Also make sure we avoid keeping password fields having - // |autocomplete='new-password'| attribute set. - if (ambiguous_and_multiple_password_fields_with_autocomplete && - AutocompleteFlagForElement(input_element) != - AutocompleteFlag::CURRENT_PASSWORD) { - continue; - } - - // Check for a non-unique match. - if (found_input) { - // For change password form keep only the first password field entry. - if (does_password_field_has_ambigous_or_empty_name) { - if (!form_util::IsWebElementVisible((*result)[field_name])) { - // If a previously chosen field was invisible then take the current - // one. - (*result)[field_name] = input_element; - } - continue; - } - - found_input = false; - break; - } - - (*result)[field_name] = input_element; - found_input = true; - } - - // A required element was not found. This is not the right form. - // Make sure no input elements from a partially matched form in this - // iteration remain in the result set. - // Note: clear will remove a reference from each InputElement. - if (!found_input) { - result->clear(); - return false; - } - - return true; -} - -// Helper to search through |control_elements| for the specified input elements -// in |data|, and add results to |result|. -bool FindFormInputElements( - const std::vector<WebFormControlElement>& control_elements, - const PasswordFormFillData& data, - bool ambiguous_or_empty_names, - FormInputElementMap* result) { - return FindFormInputElement(control_elements, data.password_field, - ambiguous_or_empty_names, result) && - (!FillDataContainsFillableUsername(data) || - FindFormInputElement(control_elements, data.username_field, - ambiguous_or_empty_names, result)); -} - -// Helper to locate form elements identified by |data|. -void FindFormElements(content::RenderFrame* render_frame, - const PasswordFormFillData& data, - bool ambiguous_or_empty_names, - FormElementsList* results) { - DCHECK(results); - - WebDocument doc = render_frame->GetWebFrame()->GetDocument(); - - if (GetSignOnRealm(data.origin) != - GetSignOnRealm(form_util::GetCanonicalOriginForDocument(doc))) - return; - - WebVector<WebFormElement> forms; - doc.Forms(forms); - - for (const WebFormElement& form : forms) { - // Action URL must match. - if (data.action != form_util::GetCanonicalActionForForm(form)) - continue; - - std::vector<WebFormControlElement> control_elements = - form_util::ExtractAutofillableElementsInForm(form); - FormInputElementMap cur_map; - if (FindFormInputElements(control_elements, data, ambiguous_or_empty_names, - &cur_map)) - results->push_back(cur_map); - } - // If the element to be filled are not in a <form> element, the "action" and - // origin should be the same. - if (data.action != data.origin) - return; - - std::vector<WebFormControlElement> control_elements = - form_util::GetUnownedAutofillableFormFieldElements(doc.All(), nullptr); - FormInputElementMap unowned_elements_map; - if (FindFormInputElements(control_elements, data, ambiguous_or_empty_names, - &unowned_elements_map)) - results->push_back(unowned_elements_map); -} - bool IsElementEditable(const WebInputElement& element) { return element.IsEnabled() && !element.IsReadOnly(); } @@ -434,32 +245,11 @@ WebString GetFormSignatureAsWebString(const PasswordForm& password_form) { base::NumberToString(CalculateFormSignature(password_form.form_data))); } -// Add parser annotations saved in |password_form| to |element|. -void AddParserAnnotations(PasswordForm* password_form, - blink::WebFormControlElement* element) { - base::string16 element_name = element->NameForAutofill().Utf16(); - std::string attribute_value; - if (password_form->username_element == element_name) { - attribute_value = "username_element"; - } else if (password_form->password_element == element_name) { - attribute_value = "password_element"; - } else if (password_form->new_password_element == element_name) { - attribute_value = "new_password_element"; - } else if (password_form->confirmation_password_element == element_name) { - attribute_value = "confirmation_password_element"; - } - element->SetAttribute( - blink::WebString::FromASCII(kDebugAttributeForParserAnnotations), - attribute_value.empty() ? blink::WebString() - : blink::WebString::FromASCII(attribute_value)); -} - // Annotate |fields| with field signatures and form signature as HTML // attributes. void AnnotateFieldsWithSignatures( std::vector<blink::WebFormControlElement>* fields, - const blink::WebString& form_signature, - PasswordForm* password_form) { + const blink::WebString& form_signature) { for (blink::WebFormControlElement& control_element : *fields) { FieldSignature field_signature = CalculateFieldSignatureByNameAndType( control_element.NameForAutofill().Utf16(), @@ -470,8 +260,6 @@ void AnnotateFieldsWithSignatures( control_element.SetAttribute( blink::WebString::FromASCII(kDebugAttributeForFormSignature), form_signature); - if (password_form) - AddParserAnnotations(password_form, &control_element); } } @@ -481,7 +269,9 @@ void AnnotateFormsAndFieldsWithSignatures(WebLocalFrame* frame, WebVector<WebFormElement>* forms) { for (WebFormElement& form : *forms) { std::unique_ptr<PasswordForm> password_form( - CreatePasswordFormFromWebForm(form, nullptr, nullptr, nullptr)); + CreateSimplifiedPasswordFormFromWebForm( + form, /*field_data_manager=*/nullptr, + /*username_detector_cache=*/nullptr)); WebString form_signature; if (password_form) { form_signature = GetFormSignatureAsWebString(*password_form); @@ -490,21 +280,20 @@ void AnnotateFormsAndFieldsWithSignatures(WebLocalFrame* frame, } std::vector<WebFormControlElement> form_fields = form_util::ExtractAutofillableElementsInForm(form); - AnnotateFieldsWithSignatures(&form_fields, form_signature, - password_form ? password_form.get() : nullptr); + AnnotateFieldsWithSignatures(&form_fields, form_signature); } std::vector<WebFormControlElement> unowned_elements = form_util::GetUnownedAutofillableFormFieldElements( frame->GetDocument().All(), nullptr); std::unique_ptr<PasswordForm> password_form( - CreatePasswordFormFromUnownedInputElements(*frame, nullptr, nullptr, - nullptr)); + CreateSimplifiedPasswordFormFromUnownedInputElements( + *frame, /*field_data_manager=*/nullptr, + /*username_detector_cache=*/nullptr)); WebString form_signature; if (password_form) form_signature = GetFormSignatureAsWebString(*password_form); - AnnotateFieldsWithSignatures(&unowned_elements, form_signature, - password_form ? password_form.get() : nullptr); + AnnotateFieldsWithSignatures(&unowned_elements, form_signature); } // Returns true iff there is a password field in |frame|. @@ -584,6 +373,30 @@ bool IsInCrossOriginIframe(const WebInputElement& element) { return false; } +// Whether any of the fields in |form) is a non-empty password field. +bool FormHasNonEmptyPasswordField(const FormData& form) { + for (const auto& field : form.fields) { + if (field.IsPasswordInputElement()) { + if (!field.value.empty() || !field.typed_value.empty()) + return true; + } + } + return false; +} + +void AnnotateFieldWithParsingResult(WebDocument doc, + uint32_t renderer_id, + const std::string& text) { + if (renderer_id == FormData::kNotSetFormRendererId) + return; + auto element = FindFormControlElementsByUniqueRendererId(doc, renderer_id); + if (element.IsNull()) + return; + element.SetAttribute( + WebString::FromASCII(kDebugAttributeForParserAnnotations), + WebString::FromASCII(text)); +} + } // namespace //////////////////////////////////////////////////////////////////////////////// @@ -940,44 +753,81 @@ bool PasswordAutofillAgent::FindPasswordInfoForElement( return true; } -bool PasswordAutofillAgent::IsUsernameOrPasswordField( +void PasswordAutofillAgent::MaybeCheckSafeBrowsingReputation( const WebInputElement& element) { + // Enabled on desktop and Android +#if BUILDFLAG(FULL_SAFE_BROWSING) || BUILDFLAG(SAFE_BROWSING_DB_REMOTE) // Note: A site may use a Password field to collect a CVV or a Credit Card // number, but showing a slightly misleading warning here is better than // showing no warning at all. - if (element.IsPasswordFieldForAutofill()) - return true; - - // If a field declares itself a username input, show the warning. - if (AutocompleteFlagForElement(element) == AutocompleteFlag::USERNAME) - return true; + if (!element.IsPasswordFieldForAutofill()) + return; + if (checked_safe_browsing_reputation_) + return; - // Otherwise, analyze the form and return true if this input element seems - // to be the username field. - std::unique_ptr<PasswordForm> password_form; - if (element.Form().IsNull()) { - // To double check that element's frame and |render_frame()->GetWebFrame()| - // which is used in |GetPasswordFormFromUnownedInputElements| are identical. - DCHECK_EQ(element.GetDocument().GetFrame(), render_frame()->GetWebFrame()); - password_form = GetPasswordFormFromUnownedInputElements(); - } else { - password_form = GetPasswordFormFromWebForm(element.Form()); - } + checked_safe_browsing_reputation_ = true; + WebLocalFrame* frame = render_frame()->GetWebFrame(); + GURL frame_url = GURL(frame->GetDocument().Url()); + GURL action_url = element.Form().IsNull() + ? GURL() + : form_util::GetCanonicalActionForForm(element.Form()); + GetPasswordManagerDriver()->CheckSafeBrowsingReputation(action_url, + frame_url); +#endif +} - if (!password_form) - return false; - return (password_form->username_element == element.NameForAutofill().Utf16()); +bool PasswordAutofillAgent::ShouldSuppressKeyboard() { + // The keyboard should be suppressed if we are showing the Touch To Fill UI. + return touch_to_fill_state_ == TouchToFillState::kIsShowing; } bool PasswordAutofillAgent::TryToShowTouchToFill( const WebFormControlElement& control_element) { - const WebInputElement* element = ToWebInputElement(&control_element); - if (!element || (!base::Contains(web_input_to_password_info_, *element) && - !base::Contains(password_to_username_, *element))) { + // Don't show Touch To Fill if it should only be enabled for insecure origins + // and we are currently on a potentially trustworthy origin. + if (base::GetFieldTrialParamByFeatureAsBool(features::kAutofillTouchToFill, + "insecure-origins-only", + /*default_value=*/false) && + render_frame() + ->GetWebFrame() + ->GetDocument() + .GetSecurityOrigin() + .IsPotentiallyTrustworthy()) { + return false; + } + + if (touch_to_fill_state_ != TouchToFillState::kShouldShow) + return false; + + const WebInputElement* input_element = ToWebInputElement(&control_element); + WebInputElement username_element; + WebInputElement password_element; + PasswordInfo* password_info = nullptr; + if (!input_element || + !FindPasswordInfoForElement(*input_element, &username_element, + &password_element, &password_info)) { return false; } + // Don't trigger Touch To Fill when there is no password element or it is not + // editable. + if (password_element.IsNull() || !IsElementEditable(password_element)) + return false; + + // Highlight the fields that are about to be filled by the user and remember + // the old autofill state of |username_element| and |password_element|. + if (IsUsernameAmendable(username_element, + input_element->IsPasswordFieldForAutofill())) { + username_autofill_state_ = username_element.GetAutofillState(); + username_element.SetAutofillState(WebAutofillState::kPreviewed); + } + + password_autofill_state_ = password_element.GetAutofillState(); + password_element.SetAutofillState(WebAutofillState::kPreviewed); + + focused_input_element_ = *input_element; GetPasswordManagerDriver()->ShowTouchToFill(); + touch_to_fill_state_ = TouchToFillState::kIsShowing; return true; } @@ -990,22 +840,7 @@ bool PasswordAutofillAgent::ShowSuggestions(const WebInputElement& element, if (!FindPasswordInfoForElement(element, &username_element, &password_element, &password_info)) { - if (IsUsernameOrPasswordField(element)) { - WebLocalFrame* frame = render_frame()->GetWebFrame(); - GURL frame_url = GURL(frame->GetDocument().Url()); -// Enabled on desktop and Android -#if BUILDFLAG(FULL_SAFE_BROWSING) || BUILDFLAG(SAFE_BROWSING_DB_REMOTE) - if (!checked_safe_browsing_reputation_) { - checked_safe_browsing_reputation_ = true; - GURL action_url = - element.Form().IsNull() - ? GURL() - : form_util::GetCanonicalActionForForm(element.Form()); - GetPasswordManagerDriver()->CheckSafeBrowsingReputation(action_url, - frame_url); - } -#endif - } + MaybeCheckSafeBrowsingReputation(element); return false; } @@ -1015,11 +850,6 @@ bool PasswordAutofillAgent::ShowSuggestions(const WebInputElement& element, return true; } - if (element.NameForAutofill().IsEmpty() && - !DoesFormContainAmbiguousOrEmptyNames(password_info->fill_data)) { - return false; // If the field has no name, then we won't have values. - } - // Don't attempt to autofill with values that are too large. if (element.Value().length() > kMaximumTextSizeForAutocomplete) return false; @@ -1034,6 +864,13 @@ bool PasswordAutofillAgent::ShowSuggestions(const WebInputElement& element, if (generation_popup_showing) return false; + // Don't call ShowSuggestionPopup if Touch To Fill is currently showing. Since + // Touch To Fill in spirit is very similar to a suggestion pop-up, return true + // so that the AutofillAgent does not try to show other autofill suggestions + // instead. + if (touch_to_fill_state_ == TouchToFillState::kIsShowing) + return true; + // Chrome should never show more than one account for a password element since // this implies that the username element cannot be modified. Thus even if // |show_all| is true, check if the element in question is a password element @@ -1059,32 +896,33 @@ void PasswordAutofillAgent::OnDynamicFormsSeen() { void PasswordAutofillAgent::FireSubmissionIfFormDisappear( SubmissionIndicatorEvent event) { - if (!provisionally_saved_form_.IsPasswordValid()) + if (!browser_has_form_to_process_) return; - DCHECK(FrameCanAccessPasswordManager()); // Prompt to save only if the form is now gone, either invisible or // removed from the DOM. WebLocalFrame* frame = render_frame()->GetWebFrame(); - const auto& password_form = provisionally_saved_form_.password_form(); // TODO(crbug.com/720347): This method could be called often and checking form - // visibility could be expesive. Add performance metrics for this. + // visibility could be expensive. Add performance metrics for this. if (event != SubmissionIndicatorEvent::DOM_MUTATION_AFTER_XHR) { - if (form_util::IsFormVisible(frame, - provisionally_saved_form_.form_element(), - password_form.action, password_form.origin, - password_form.form_data) || - (provisionally_saved_form_.form_element().IsNull() && - IsUnownedPasswordFormVisible( - provisionally_saved_form_.input_element()))) { - return; + bool is_last_updated_field_in_form = + last_updated_form_renderer_id_ != FormData::kNotSetFormRendererId; + // Check whether the form which is the candidate for submission disappeared. + // If yes this form is considered to be successfully submitted. + if (is_last_updated_field_in_form) { + // A form is inside <form> tag. Check the visibility of the whole form. + if (IsFormVisible(frame, last_updated_form_renderer_id_)) + return; + } else { + // A form is without <form> tag. Check the visibility of the last updated + // field. + if (IsFormControlVisible(frame, last_updated_field_renderer_id_)) + return; } } - - provisionally_saved_form_.SetSubmissionIndicatorEvent(event); - GetPasswordManagerDriver()->SameDocumentNavigation(password_form); - provisionally_saved_form_.Reset(); + GetPasswordManagerDriver()->SameDocumentNavigation(event); + browser_has_form_to_process_ = false; } void PasswordAutofillAgent::UserGestureObserved() { @@ -1268,13 +1106,10 @@ void PasswordAutofillAgent::OnFrameDetached() { // If a sub frame has been destroyed while the user was entering information // into a password form, try to save the data. See https://crbug.com/450806 // for examples of sites that perform login using this technique. - if (render_frame()->GetWebFrame()->Parent() && - provisionally_saved_form_.IsPasswordValid()) { + if (render_frame()->GetWebFrame()->Parent() && browser_has_form_to_process_) { DCHECK(FrameCanAccessPasswordManager()); - provisionally_saved_form_.SetSubmissionIndicatorEvent( - SubmissionIndicatorEvent::FRAME_DETACHED); GetPasswordManagerDriver()->SameDocumentNavigation( - provisionally_saved_form_.password_form()); + SubmissionIndicatorEvent::FRAME_DETACHED); } CleanupOnDocumentShutdown(); } @@ -1291,18 +1126,6 @@ void PasswordAutofillAgent::OnWillSubmitForm(const WebFormElement& form) { std::unique_ptr<PasswordForm> submitted_form = GetPasswordFormFromWebForm(form); - // As a site may clear field values, use a provisionally saved form as - // the submitted form. - if (provisionally_saved_form_.IsSet() && - (!submitted_form || - submitted_form->action == - provisionally_saved_form_.password_form().action)) { - submitted_form.reset( - new PasswordForm(provisionally_saved_form_.password_form())); - if (logger) - logger->LogMessage(Logger::STRING_SUBMITTED_PASSWORD_REPLACED); - } - // If there is a provisionally saved password, copy over the previous // password value so we get the user's typed password, not the value that // may have been transformed for submit. @@ -1328,8 +1151,7 @@ void PasswordAutofillAgent::OnWillSubmitForm(const WebFormElement& form) { if (logger) logger->LogMessage(Logger::STRING_SECURITY_ORIGIN_FAILURE); } - - provisionally_saved_form_.Reset(); + browser_has_form_to_process_ = false; } else if (logger) { logger->LogMessage(Logger::STRING_FORM_IS_NOT_PASSWORD); } @@ -1362,37 +1184,12 @@ void PasswordAutofillAgent::ReadyToCommitNavigation( CleanupOnDocumentShutdown(); } -void PasswordAutofillAgent::OnProbablyFormSubmitted() { - std::unique_ptr<RendererSavePasswordProgressLogger> logger; - if (logging_state_active_) { - logger.reset(new RendererSavePasswordProgressLogger( - GetPasswordManagerDriver().get())); - logger->LogMessage(Logger::STRING_DID_START_PROVISIONAL_LOAD_METHOD); - } - - if (!FrameCanAccessPasswordManager()) { - if (logger) - logger->LogMessage(Logger::STRING_SECURITY_ORIGIN_FAILURE); - return; - } - - // If onsubmit has been called, try and save that form. - if (provisionally_saved_form_.IsSet()) { - if (logger) { - logger->LogPasswordForm(Logger::STRING_PROVISIONALLY_SAVED_FORM_FOR_FRAME, - provisionally_saved_form_.password_form()); - } - provisionally_saved_form_.SetSubmissionIndicatorEvent( - SubmissionIndicatorEvent:: - PROVISIONALLY_SAVED_FORM_ON_START_PROVISIONAL_LOAD); - GetPasswordManagerDriver()->PasswordFormSubmitted( - provisionally_saved_form_.password_form()); - provisionally_saved_form_.Reset(); - } -} +void PasswordAutofillAgent::OnProbablyFormSubmitted() {} -void PasswordAutofillAgent::FillUsingRendererIDs( +// mojom::PasswordAutofillAgent: +void PasswordAutofillAgent::FillPasswordForm( const PasswordFormFillData& form_data) { + DCHECK(form_data.has_renderer_ids); std::unique_ptr<RendererSavePasswordProgressLogger> logger; if (logging_state_active_) { logger.reset(new RendererSavePasswordProgressLogger( @@ -1440,114 +1237,57 @@ void PasswordAutofillAgent::FillUsingRendererIDs( logger.get()); } -// mojom::PasswordAutofillAgent: -void PasswordAutofillAgent::FillPasswordForm( - const PasswordFormFillData& form_data) { - if (form_data.has_renderer_ids) { - FillUsingRendererIDs(form_data); - return; - } +void PasswordAutofillAgent::SetLoggingState(bool active) { + logging_state_active_ = active; +} - std::vector<WebInputElement> elements; - std::unique_ptr<RendererSavePasswordProgressLogger> logger; - if (logging_state_active_) { - logger.reset(new RendererSavePasswordProgressLogger( - GetPasswordManagerDriver().get())); - logger->LogMessage(Logger::STRING_ON_FILL_PASSWORD_FORM_METHOD); - } - GetFillableElementFromFormData(form_data, logger.get(), &elements); +void PasswordAutofillAgent::TouchToFillClosed(bool show_virtual_keyboard) { + touch_to_fill_state_ = TouchToFillState::kWasShown; - // If wait_for_username is true, we don't want to initially fill the form - // until the user types in a valid username. - if (form_data.wait_for_username) { - LogFirstFillingResult(form_data, FillingResult::kWaitForUsername); + // Clear the autofill state from the username and password element. Note that + // we don't make use of ClearPreview() here, since this is considering the + // elements' SuggestedValue(), which Touch To Fill does not set. + DCHECK(!focused_input_element_.IsNull()); + WebInputElement username_element; + WebInputElement password_element; + PasswordInfo* password_info = nullptr; + if (!FindPasswordInfoForElement(focused_input_element_, &username_element, + &password_element, &password_info)) { return; } - if (elements.empty()) - LogFirstFillingResult(form_data, FillingResult::kNoFillableElementsFound); - - for (auto element : elements) { - WebInputElement username_element = !element.IsPasswordFieldForAutofill() - ? element - : password_to_username_[element]; - WebInputElement password_element = - element.IsPasswordFieldForAutofill() - ? element - : web_input_to_password_info_[element].password_field; - FillUserNameAndPassword(username_element, password_element, form_data, - logger.get()); - } -} - -void PasswordAutofillAgent::GetFillableElementFromFormData( - const PasswordFormFillData& form_data, - RendererSavePasswordProgressLogger* logger, - std::vector<WebInputElement>* elements) { - DCHECK(elements); - bool ambiguous_or_empty_names = - DoesFormContainAmbiguousOrEmptyNames(form_data); - FormElementsList forms; - FindFormElements(render_frame(), form_data, ambiguous_or_empty_names, &forms); - if (logger) { - logger->LogBoolean(Logger::STRING_AMBIGUOUS_OR_EMPTY_NAMES, - ambiguous_or_empty_names); - logger->LogNumber(Logger::STRING_NUMBER_OF_POTENTIAL_FORMS_TO_FILL, - forms.size()); - logger->LogBoolean(Logger::STRING_FORM_DATA_WAIT, - form_data.wait_for_username); - } - for (const auto& form : forms) { - base::string16 username_field_name; - base::string16 password_field_name = - FieldName(form_data.password_field, ambiguous_or_empty_names); - bool form_contains_fillable_username_field = - FillDataContainsFillableUsername(form_data); - if (form_contains_fillable_username_field) { - username_field_name = - FieldName(form_data.username_field, ambiguous_or_empty_names); - } - if (logger) { - logger->LogBoolean(Logger::STRING_CONTAINS_FILLABLE_USERNAME_FIELD, - form_contains_fillable_username_field); - logger->LogBoolean(Logger::STRING_USERNAME_FIELD_NAME_EMPTY, - username_field_name.empty()); - logger->LogBoolean(Logger::STRING_PASSWORD_FIELD_NAME_EMPTY, - password_field_name.empty()); - } - - // Attach autocomplete listener to enable selecting alternate logins. - WebInputElement username_element; - WebInputElement password_element; - - // Check whether the password form has a username input field. - if (!username_field_name.empty()) { - const auto it = form.find(username_field_name); - DCHECK(it != form.end()); - username_element = it->second; - } - - // No password field, bail out. - if (password_field_name.empty()) - break; + if (!username_element.IsNull()) + username_element.SetAutofillState(username_autofill_state_); - // Get pointer to password element. (We currently only support single - // password forms). - { - const auto it = form.find(password_field_name); - DCHECK(it != form.end()); - password_element = it->second; + if (!password_element.IsNull()) + password_element.SetAutofillState(password_autofill_state_); + + if (show_virtual_keyboard) { + render_frame()->ShowVirtualKeyboard(); + + // Since Touch To Fill suppresses the Autofill popup, re-trigger the + // suggestions in case the virtual keyboard should be shown. This is limited + // to the keyboard accessory, as otherwise it would result in a flickering + // of the popup, due to showing the keyboard at the same time. + if (IsKeyboardAccessoryEnabled()) { + ShowSuggestions(focused_input_element_, /*show_all=*/false, + /*generation_popup_showing=*/false); } - - WebInputElement main_element = - username_element.IsNull() ? password_element : username_element; - if (elements) - elements->push_back(main_element); - StoreDataForFillOnAccountSelect(form_data, username_element, - password_element); } +} - MaybeStoreFallbackData(form_data); +void PasswordAutofillAgent::AnnotateFieldsWithParsingResult( + const ParsingResult& parsing_result) { + WebDocument doc = render_frame()->GetWebFrame()->GetDocument(); + AnnotateFieldWithParsingResult(doc, parsing_result.username_renderer_id, + "username_element"); + AnnotateFieldWithParsingResult(doc, parsing_result.password_renderer_id, + "password_element"); + AnnotateFieldWithParsingResult(doc, parsing_result.new_password_renderer_id, + "new_password_element"); + AnnotateFieldWithParsingResult(doc, + parsing_result.confirm_password_renderer_id, + "confirmation_password_element"); } void PasswordAutofillAgent::FocusedNodeHasChanged(const blink::WebNode& node) { @@ -1595,16 +1335,15 @@ void PasswordAutofillAgent::FocusedNodeHasChanged(const blink::WebNode& node) { std::unique_ptr<PasswordForm> PasswordAutofillAgent::GetPasswordFormFromWebForm( const WebFormElement& web_form) { - return CreatePasswordFormFromWebForm(web_form, &field_data_manager_, - &form_predictions_, - &username_detector_cache_); + return CreateSimplifiedPasswordFormFromWebForm(web_form, &field_data_manager_, + &username_detector_cache_); } std::unique_ptr<PasswordForm> PasswordAutofillAgent::GetSimplifiedPasswordFormFromWebForm( const WebFormElement& web_form) { - return CreateSimplifiedPasswordFormFromWebForm(web_form, - &field_data_manager_); + return CreateSimplifiedPasswordFormFromWebForm(web_form, &field_data_manager_, + &username_detector_cache_); } std::unique_ptr<PasswordForm> @@ -1619,9 +1358,8 @@ PasswordAutofillAgent::GetPasswordFormFromUnownedInputElements() { WebLocalFrame* web_frame = frame->GetWebFrame(); if (!web_frame) return nullptr; - return CreatePasswordFormFromUnownedInputElements( - *web_frame, &field_data_manager_, &form_predictions_, - &username_detector_cache_); + return CreateSimplifiedPasswordFormFromUnownedInputElements( + *web_frame, &field_data_manager_, &username_detector_cache_); } std::unique_ptr<PasswordForm> @@ -1633,17 +1371,7 @@ PasswordAutofillAgent::GetSimplifiedPasswordFormFromUnownedInputElements() { if (!web_frame) return nullptr; return CreateSimplifiedPasswordFormFromUnownedInputElements( - *web_frame, &field_data_manager_); -} - -// mojom::PasswordAutofillAgent: -void PasswordAutofillAgent::SetLoggingState(bool active) { - logging_state_active_ = active; -} - -void PasswordAutofillAgent::AutofillUsernameAndPasswordDataReceived( - const FormsPredictionsMap& predictions) { - form_predictions_.insert(predictions.begin(), predictions.end()); + *web_frame, &field_data_manager_, &username_detector_cache_); } //////////////////////////////////////////////////////////////////////////////// @@ -1685,7 +1413,7 @@ bool PasswordAutofillAgent::ShowSuggestionPopup( GetPasswordManagerDriver()->ShowPasswordSuggestions( field.text_direction, username_string, options, - render_frame()->GetRenderView()->ElementBoundsInWindow(user_input)); + render_frame()->ElementBoundsInWindow(user_input)); username_query_prefix_ = username_string; return CanShowSuggestion(password_info.fill_data, username_string, show_all); } @@ -1694,17 +1422,19 @@ void PasswordAutofillAgent::CleanupOnDocumentShutdown() { web_input_to_password_info_.clear(); password_to_username_.clear(); last_supplied_password_info_iter_ = web_input_to_password_info_.end(); - provisionally_saved_form_.Reset(); + browser_has_form_to_process_ = false; field_data_manager_.ClearData(); username_autofill_state_ = WebAutofillState::kNotFilled; password_autofill_state_ = WebAutofillState::kNotFilled; sent_request_to_store_ = false; checked_safe_browsing_reputation_ = false; username_query_prefix_.clear(); - form_predictions_.clear(); username_detector_cache_.clear(); forms_structure_cache_.clear(); autofilled_elements_cache_.clear(); + last_updated_field_renderer_id_ = FormData::kNotSetFormRendererId; + last_updated_form_renderer_id_ = FormData::kNotSetFormRendererId; + touch_to_fill_state_ = TouchToFillState::kShouldShow; #if !defined(OS_ANDROID) && !defined(OS_IOS) page_passwords_analyser_.Reset(); #endif @@ -1729,6 +1459,7 @@ void PasswordAutofillAgent::ProvisionallySavePassword( ProvisionallySaveRestriction restriction) { DCHECK(!form.IsNull() || !element.IsNull()); + SetLastUpdatedFormAndField(form, element); std::unique_ptr<PasswordForm> password_form; if (form.IsNull()) { password_form = GetPasswordFormFromUnownedInputElements(); @@ -1738,22 +1469,19 @@ void PasswordAutofillAgent::ProvisionallySavePassword( if (!password_form) return; - bool has_password = !password_form->password_value.empty() || - !password_form->new_password_value.empty(); + bool has_password = FormHasNonEmptyPasswordField(password_form->form_data); if (restriction == RESTRICTION_NON_EMPTY_PASSWORD && !has_password) return; if (!FrameCanAccessPasswordManager()) return; - provisionally_saved_form_.Set(std::move(password_form), form, element); - if (has_password) { - GetPasswordManagerDriver()->ShowManualFallbackForSaving( - provisionally_saved_form_.password_form()); + GetPasswordManagerDriver()->ShowManualFallbackForSaving(*password_form); } else { GetPasswordManagerDriver()->HideManualFallbackForSaving(); } + browser_has_form_to_process_ = true; } bool PasswordAutofillAgent::FillUserNameAndPassword( @@ -1914,19 +1642,6 @@ void PasswordAutofillAgent::OnProvisionallySaveForm( } DCHECK_EQ(ElementChangeSource::WILL_SEND_SUBMIT_EVENT, source); - // Forms submitted via XHR are not seen by WillSubmitForm if the default - // onsubmit handler is overridden. Such submission first gets detected in - // DidStartProvisionalLoad, which no longer knows about the particular form, - // and uses the candidate stored in |provisionally_saved_form_|. - // - // User-typed password will get stored to |provisionally_saved_form_| in - // TextDidChangeInTextField. Autofilled or JavaScript-copied passwords need to - // be saved here. - // - // Only non-empty passwords are saved here. Empty passwords were likely - // cleared by some scripts (http://crbug.com/28910, http://crbug.com/391693). - // Had the user cleared the password, |provisionally_saved_form_| would - // already have been updated in TextDidChangeInTextField. ProvisionallySavePassword(form, input_element, RESTRICTION_NON_EMPTY_PASSWORD); } @@ -2042,10 +1757,9 @@ void PasswordAutofillAgent::LogFirstFillingResult( return; UMA_HISTOGRAM_ENUMERATION("PasswordManager.FirstRendererFillingResult", result); - if (form_data.has_renderer_ids) { - GetPasswordManagerDriver()->LogFirstFillingResult( - form_data.form_renderer_id, base::strict_cast<int32_t>(result)); - } + DCHECK(form_data.has_renderer_ids); + GetPasswordManagerDriver()->LogFirstFillingResult( + form_data.form_renderer_id, base::strict_cast<int32_t>(result)); recorded_first_filling_result_ = true; } @@ -2132,4 +1846,15 @@ void PasswordAutofillAgent::AutofillField(const base::string16& value, WebString::FromUTF16(value)); } +void PasswordAutofillAgent::SetLastUpdatedFormAndField( + const WebFormElement& form, + const WebFormControlElement& input) { + last_updated_form_renderer_id_ = form.IsNull() + ? FormData::kNotSetFormRendererId + : form.UniqueRendererFormId(); + last_updated_field_renderer_id_ = input.IsNull() + ? FormData::kNotSetFormRendererId + : input.UniqueRendererFormControlId(); +} + } // 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 6346b5f2d92..81b0e38fb0e 100644 --- a/chromium/components/autofill/content/renderer/password_autofill_agent.h +++ b/chromium/components/autofill/content/renderer/password_autofill_agent.h @@ -20,11 +20,8 @@ #include "components/autofill/content/renderer/field_data_manager.h" #include "components/autofill/content/renderer/form_tracker.h" #include "components/autofill/content/renderer/html_based_username_detector.h" -#include "components/autofill/content/renderer/provisionally_saved_password_form.h" -#include "components/autofill/core/common/form_data_predictions.h" #include "components/autofill/core/common/mojom/autofill_types.mojom.h" #include "components/autofill/core/common/password_form.h" -#include "components/autofill/core/common/password_form_field_prediction_map.h" #include "components/autofill/core/common/password_form_fill_data.h" #include "content/public/renderer/render_frame_observer.h" #include "content/public/renderer/render_view_observer.h" @@ -132,8 +129,9 @@ class PasswordAutofillAgent : public content::RenderFrameObserver, void FillIntoFocusedField(bool is_password, const base::string16& credential) override; void SetLoggingState(bool active) override; - void AutofillUsernameAndPasswordDataReceived( - const FormsPredictionsMap& predictions) override; + void TouchToFillClosed(bool show_virtual_keyboard) override; + void AnnotateFieldsWithParsingResult( + const ParsingResult& parsing_result) override; // FormTracker::Observer void OnProvisionallySaveForm(const blink::WebFormElement& form, @@ -177,8 +175,12 @@ class PasswordAutofillAgent : public content::RenderFrameObserver, bool DidClearAutofillSelection( const blink::WebFormControlElement& control_element); - // Returns whether the element is a username or password textfield. - bool IsUsernameOrPasswordField(const blink::WebInputElement& element); + // Sends a reputation check request in case if |element| has type password and + // no check request were sent from this frame load. + void MaybeCheckSafeBrowsingReputation(const blink::WebInputElement& element); + + // Returns whether the soft keyboard should be suppressed. + bool ShouldSuppressKeyboard(); // Asks the agent to show the touch to fill UI for |control_element|. Returns // whether the agent was able to do so. @@ -196,11 +198,6 @@ class PasswordAutofillAgent : public content::RenderFrameObserver, bool show_all, bool generation_popup_showing); - // Shows an Autofill-style popup with a warning that the form is not secure. - // This UI is shown when a username or password field is autofilled or edited - // on a non-secure page. - void ShowNotSecureWarning(const blink::WebInputElement& element); - // Called when new form controls are inserted. void OnDynamicFormsSeen(); @@ -209,15 +206,6 @@ class PasswordAutofillAgent : public content::RenderFrameObserver, // JavaScript. void UserGestureObserved(); - // Given password form data |form_data| returns a set of WebInputElements in - // |elements|, which must be non-null, that the password manager has values - // for filling. Also takes an optional logger |logger| for logging password - // autofill behavior. - void GetFillableElementFromFormData( - const PasswordFormFillData& form_data, - RendererSavePasswordProgressLogger* logger, - std::vector<blink::WebInputElement>* elements); - // Called when the focused node has changed. This is not called if the focus // moves outside the frame. void FocusedNodeHasChanged(const blink::WebNode& node); @@ -262,6 +250,16 @@ class PasswordAutofillAgent : public content::RenderFrameObserver, RESTRICTION_NON_EMPTY_PASSWORD }; + // Enumeration representing possible Touch To Fill states. This is used to + // make sure that Touch To Fill will only be shown in response to the first + // password form focus during a frame's life time and to suppress the soft + // keyboard when Touch To Fill is shown. + enum class TouchToFillState { + kShouldShow, + kIsShowing, + kWasShown, + }; + struct PasswordInfo { blink::WebInputElement password_field; PasswordFormFillData fill_data; @@ -422,10 +420,6 @@ class PasswordAutofillAgent : public content::RenderFrameObserver, void HidePopup(); - // TODO(https://crbug.com/831123): Rename to FillPasswordForm when browser - // form parsing is launched. - void FillUsingRendererIDs(const PasswordFormFillData& form_data); - // Returns pair(username_element, password_element) based on renderer ids from // |username_field| and |password_field| from |form_data|. std::pair<blink::WebInputElement, blink::WebInputElement> @@ -466,6 +460,9 @@ class PasswordAutofillAgent : public content::RenderFrameObserver, // non-null. void AutofillField(const base::string16& value, blink::WebInputElement field); + void SetLastUpdatedFormAndField(const blink::WebFormElement& form, + const blink::WebFormControlElement& input); + // 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_|. @@ -473,10 +470,6 @@ class PasswordAutofillAgent : public content::RenderFrameObserver, // The chronologically last insertion into |web_input_to_password_info_|. WebInputToPasswordInfoMap::iterator last_supplied_password_info_iter_; - // Set if the user might be submitting a password form on the current page, - // but the submit may still fail (i.e. doesn't pass JavaScript validation). - ProvisionallySavedPasswordForm provisionally_saved_form_; - // 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 @@ -504,16 +497,18 @@ class PasswordAutofillAgent : public content::RenderFrameObserver, // True indicates that a request for credentials has been sent to the store. bool sent_request_to_store_; + // True indicates that a form data has been sent to the browser process. Gets + // cleared when the form is submitted to indicate that the browser has already + // processed the form. + // TODO(crbug.com/949519): double check if we need this variable. + bool browser_has_form_to_process_ = false; + // True indicates that a safe browsing reputation check has been triggered. bool checked_safe_browsing_reputation_; // Records the username typed before suggestions preview. base::string16 username_query_prefix_; - // Contains server predictions for username, password and/or new password - // fields for individual forms. - FormsPredictionsMap form_predictions_; - // The HTML based username detector's cache which maps form elements to // username predictions. UsernameDetectorCache username_detector_cache_; @@ -550,6 +545,15 @@ class PasswordAutofillAgent : public content::RenderFrameObserver, // DidCommitProvisionalLoad() but only for non-same-document-navigations. bool recorded_first_filling_result_ = false; + // Contains renderer id of last updated input element. + uint32_t last_updated_field_renderer_id_ = FormData::kNotSetFormRendererId; + // Contains renderer id of the form of the last updated input element. + uint32_t last_updated_form_renderer_id_ = FormData::kNotSetFormRendererId; + + // Current state of Touch To Fill. This is reset during + // CleanupOnDocumentShutdown. + TouchToFillState touch_to_fill_state_ = TouchToFillState::kShouldShow; + DISALLOW_COPY_AND_ASSIGN(PasswordAutofillAgent); }; 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 9e6c94e1581..c60a5360fb6 100644 --- a/chromium/components/autofill/content/renderer/password_form_conversion_utils.cc +++ b/chromium/components/autofill/content/renderer/password_form_conversion_utils.cc @@ -4,34 +4,17 @@ #include "components/autofill/content/renderer/password_form_conversion_utils.h" -#include <stddef.h> - -#include <algorithm> -#include <set> -#include <string> - -#include "base/i18n/case_conversion.h" #include "base/lazy_instance.h" #include "base/macros.h" -#include "base/metrics/histogram_macros.h" #include "base/no_destructor.h" -#include "base/stl_util.h" -#include "base/strings/string16.h" #include "base/strings/string_piece.h" #include "base/strings/string_split.h" -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.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/autofill_regex_constants.h" -#include "components/autofill/core/common/autofill_regexes.h" -#include "components/autofill/core/common/autofill_util.h" #include "components/autofill/core/common/password_form.h" -#include "components/autofill/core/common/password_form_field_prediction_map.h" #include "google_apis/gaia/gaia_urls.h" #include "net/base/url_util.h" #include "third_party/blink/public/platform/web_string.h" -#include "third_party/blink/public/platform/web_vector.h" #include "third_party/blink/public/web/web_document.h" #include "third_party/blink/public/web/web_form_control_element.h" #include "third_party/blink/public/web/web_input_element.h" @@ -48,217 +31,8 @@ using blink::WebString; namespace autofill { -using mojom::PasswordFormFieldPredictionType; - namespace { -constexpr char kAutocompleteUsername[] = "username"; -constexpr char kAutocompleteCurrentPassword[] = "current-password"; -constexpr char kAutocompleteNewPassword[] = "new-password"; -constexpr char kAutocompleteCreditCardPrefix[] = "cc-"; - -// Parses the string with the value of an autocomplete attribute. If any of the -// tokens "username", "current-password" or "new-password" are present, returns -// an appropriate enum value, picking an arbitrary one if more are applicable. -// Otherwise, it returns CREDIT_CARD if a token with a "cc-" prefix is found. -// Otherwise, returns NONE. -AutocompleteFlag ExtractAutocompleteFlag(const std::string& attribute) { - std::vector<base::StringPiece> tokens = - base::SplitStringPiece(attribute, base::kWhitespaceASCII, - base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); - bool cc_seen = false; - for (base::StringPiece token : tokens) { - if (base::LowerCaseEqualsASCII(token, kAutocompleteUsername)) - return AutocompleteFlag::USERNAME; - if (base::LowerCaseEqualsASCII(token, kAutocompleteCurrentPassword)) - return AutocompleteFlag::CURRENT_PASSWORD; - if (base::LowerCaseEqualsASCII(token, kAutocompleteNewPassword)) - return AutocompleteFlag::NEW_PASSWORD; - - if (!cc_seen) { - cc_seen = base::StartsWith(token, kAutocompleteCreditCardPrefix, - base::CompareCase::SENSITIVE); - } - } - return cc_seen ? AutocompleteFlag::CREDIT_CARD : AutocompleteFlag::NONE; -} - -// Helper to spare map::find boilerplate when caching field's autocomplete -// attributes. -class AutocompleteCache { - public: - AutocompleteCache(); - - ~AutocompleteCache(); - - // Computes and stores the AutocompleteFlag for |field| based on its - // autocomplete attribute. Note that this cannot be done on-demand during - // RetrieveFor, because the cache spares space and look-up time by not storing - // AutocompleteFlag::NONE values, hence for all elements without an - // autocomplete attribute, every retrieval would result in a new computation. - void Store(const FormFieldData* field); - - // Retrieves the value previously stored for |field|. - AutocompleteFlag RetrieveFor(const FormFieldData* field) const; - - private: - std::map<const FormFieldData*, AutocompleteFlag> cache_; - - DISALLOW_COPY_AND_ASSIGN(AutocompleteCache); -}; - -AutocompleteCache::AutocompleteCache() = default; - -AutocompleteCache::~AutocompleteCache() = default; - -void AutocompleteCache::Store(const FormFieldData* field) { - const AutocompleteFlag flag = - ExtractAutocompleteFlag(field->autocomplete_attribute); - // Only store non-trivial flags. Most of the elements will have the NONE - // value, so spare storage and lookup time by assuming anything not stored in - // |cache_| has the NONE flag. - if (flag != AutocompleteFlag::NONE) - cache_[field] = flag; -} - -AutocompleteFlag AutocompleteCache::RetrieveFor( - const FormFieldData* field) const { - auto it = cache_.find(field); - if (it == cache_.end()) - return AutocompleteFlag::NONE; - return it->second; -} - -// Describes fields filtering criteria. More priority criteria has higher value -// in the enum. The fields with the maximal criteria are considered in a form, -// others are ignored. Criteria for password and username fields are calculated -// separately. For example, if there is a password field with user input, the -// password fields without user input are ignored (independently whether the -// fields are visible or not). -enum class FieldFilteringLevel { - NO_FILTER = 0, - VISIBILITY = 1, - USER_INPUT = 2 -}; - -// Helper to determine which password is the main (current) one, and which is -// the new password (e.g., on a sign-up or change password form), if any. If the -// new password is found and there is another password field with the same user -// input, the function also sets |confirmation_password| to this field. -void LocateSpecificPasswords(std::vector<const FormFieldData*> passwords, - const FormFieldData** current_password, - const FormFieldData** new_password, - const FormFieldData** confirmation_password, - const AutocompleteCache& autocomplete_cache) { - DCHECK(!passwords.empty()); - DCHECK(current_password && !*current_password); - DCHECK(new_password && !*new_password); - DCHECK(confirmation_password && !*confirmation_password); - - // First, look for elements marked with either autocomplete='current-password' - // or 'new-password' -- if we find any, take the hint, and treat the first of - // each kind as the element we are looking for. - for (const FormFieldData* password : passwords) { - const AutocompleteFlag flag = autocomplete_cache.RetrieveFor(password); - if (flag == AutocompleteFlag::CURRENT_PASSWORD && !*current_password) { - *current_password = password; - } else if (flag == AutocompleteFlag::NEW_PASSWORD && !*new_password) { - *new_password = password; - } else if (*new_password && ((*new_password)->value == password->value)) { - *confirmation_password = password; - } - } - - // If we have seen an element with either of autocomplete attributes above, - // take that as a signal that the page author must have intentionally left the - // rest of the password fields unmarked. Perhaps they are used for other - // purposes, e.g., PINs, OTPs, and the like. So we skip all the heuristics we - // normally do, and ignore the rest of the password fields. - if (*current_password || *new_password) - return; - - switch (passwords.size()) { - case 1: - // Single password, easy. - *current_password = passwords[0]; - break; - case 2: - if (!passwords[0]->value.empty() && - passwords[0]->value == passwords[1]->value) { - // Two identical non-empty passwords: assume we are seeing a new - // password with a confirmation. This can be either a sign-up form or a - // password change form that does not ask for the old password. - *new_password = passwords[0]; - *confirmation_password = passwords[1]; - } else { - // Assume first is old password, second is new (no choice but to guess). - // This case also includes empty passwords in order to allow filling of - // password change forms (that also could autofill for sign up form, but - // we can't do anything with this using only client side information). - *current_password = passwords[0]; - *new_password = passwords[1]; - } - break; - default: - if (!passwords[0]->value.empty() && - passwords[0]->value == passwords[1]->value && - passwords[0]->value == passwords[2]->value) { - // All three passwords are the same and non-empty? It may be a change - // password form where old and new passwords are the same. It doesn't - // matter what field is correct, let's save the value. - *current_password = passwords[0]; - } else if (passwords[1]->value == passwords[2]->value) { - // New password is the duplicated one, and comes second; or empty form - // with 3 password fields, in which case we will assume this layout. - *current_password = passwords[0]; - *new_password = passwords[1]; - *confirmation_password = passwords[2]; - } else if (passwords[0]->value == passwords[1]->value) { - // It is strange that the new password comes first, but trust more which - // fields are duplicated than the ordering of fields. Assume that - // any password fields after the new password contain sensitive - // information that isn't actually a password (security hint, SSN, etc.) - *new_password = passwords[0]; - *confirmation_password = passwords[1]; - } else { - // Three different passwords, or first and last match with middle - // different. No idea which is which. Let's save the first password. - // Password selection in a prompt will allow to correct the choice. - *current_password = passwords[0]; - } - } -} - -void FindPredictedElements( - const FormData& form_data, - const FormsPredictionsMap& form_predictions, - std::map<const FormFieldData*, PasswordFormFieldPredictionType>* - predicted_fields) { - // Matching only requires that action and name of the form match to allow - // the username to be updated even if the form is changed after page load. - // See https://crbug.com/476092 for more details. - auto field_predictions = std::find_if( - form_predictions.begin(), form_predictions.end(), - [&form_data](const auto& form_predictions_pair) { - return form_predictions_pair.first.action == form_data.action && - form_predictions_pair.first.name == form_data.name; - }); - - if (field_predictions == form_predictions.end()) - return; - - for (const auto& prediction : field_predictions->second) { - const FormFieldData& target_field = prediction.first; - const PasswordFormFieldPredictionType& type = prediction.second; - for (const FormFieldData& field : form_data.fields) { - if (field.name == target_field.name) { - (*predicted_fields)[&field] = type; - break; - } - } - } -} - const char kPasswordSiteUrlRegex[] = "passwords(?:-[a-z-]+\\.corp)?\\.google\\.com"; @@ -272,116 +46,6 @@ struct PasswordSiteUrlLazyInstanceTraits base::LazyInstance<re2::RE2, PasswordSiteUrlLazyInstanceTraits> g_password_site_matcher = LAZY_INSTANCE_INITIALIZER; -// Returns the |input_field| name if its non-empty; otherwise a |dummy_name|. -base::string16 FieldName(const FormFieldData* input_field, - const char* dummy_name) { - return input_field->name.empty() ? base::ASCIIToUTF16(dummy_name) - : input_field->name; -} - -// Return the maximal filtering criteria that |field| passes. -// If |ignore_autofilled_values|, autofilled value isn't considered user input. -FieldFilteringLevel GetFiltertingLevelForField(const FormFieldData& field, - bool ignore_autofilled_values) { - FieldPropertiesMask user_input_mask = - ignore_autofilled_values - ? FieldPropertiesFlags::USER_TYPED - : FieldPropertiesFlags::USER_TYPED | FieldPropertiesFlags::AUTOFILLED; - if (field.properties_mask & user_input_mask) - return FieldFilteringLevel::USER_INPUT; - return field.is_focusable ? FieldFilteringLevel::VISIBILITY - : FieldFilteringLevel::NO_FILTER; -} - -// Calculates the maximal filtering levels for password and username fields and -// saves them to |username_fields_level| and |password_fields_level|. The -// criteria for username fields considers only the fields before the first -// password field that has the maximal filtering level. -void GetFieldFilteringLevels(const std::vector<FormFieldData>& fields, - FieldFilteringLevel* username_fields_level, - FieldFilteringLevel* password_fields_level) { - DCHECK(password_fields_level); - DCHECK(username_fields_level); - *username_fields_level = FieldFilteringLevel::NO_FILTER; - *password_fields_level = FieldFilteringLevel::NO_FILTER; - - FieldFilteringLevel max_level_found_for_username_fields = - FieldFilteringLevel::NO_FILTER; - for (const FormFieldData& field : fields) { - if (!field.is_enabled || !field.IsTextInputElement()) - continue; - - // TODO(crbug.com/789917): Ignore autofilled values here because if there - // are only autofilled values then a form may not be filled completely (i.e. - // some user input is still expected). So, user input shouldn't be used for - // fields filtering. Once the bug is resolved, autofilled values will not be - // ignored. - FieldFilteringLevel current_field_filtering_level = - GetFiltertingLevelForField(field, true /* ignore_autofilled_values */); - - if (field.form_control_type == "password") { - if (*password_fields_level < current_field_filtering_level) { - *password_fields_level = current_field_filtering_level; - *username_fields_level = max_level_found_for_username_fields; - } - } else { - max_level_found_for_username_fields = std::max( - max_level_found_for_username_fields, current_field_filtering_level); - } - } -} - -ValueElementPair MakePossibleUsernamePair(const FormFieldData* input) { - base::string16 trimmed_input_value; - base::TrimString(input->value, base::ASCIIToUTF16(" "), &trimmed_input_value); - return {trimmed_input_value, input->name}; -} - -bool StringMatchesCVC(const base::string16& str) { - static const base::NoDestructor<base::string16> kCardCvcReCached( - base::UTF8ToUTF16(kCardCvcRe)); - - return MatchesPattern(str, *kCardCvcReCached); -} - -// Which types of password fields are present in a form? -enum class PasswordContents { - kEnabled, // At least one enabled password field. - kOnlyDisabled, // At least one password field, but not enabled. - kNone // No password fields present. -}; - -// Returns the PasswordContents reflecting the contents of |fields|. -PasswordContents GetPasswordContents(const std::vector<FormFieldData>& fields) { - PasswordContents result = PasswordContents::kNone; - for (const FormFieldData& field : fields) { - if (field.form_control_type != "password") - continue; - result = PasswordContents::kOnlyDisabled; - if (field.is_enabled) - return PasswordContents::kEnabled; - } - return result; -} - -// Find the first element in |username_predictions| (i.e. the most reliable -// prediction) that occurs in |possible_usernames|. -const FormFieldData* FindUsernameInPredictions( - const std::vector<uint32_t>& username_predictions, - const std::vector<const FormFieldData*>& possible_usernames) { - for (uint32_t predicted_id : username_predictions) { - auto iter = - std::find_if(possible_usernames.begin(), possible_usernames.end(), - [predicted_id](const FormFieldData* field) { - return field->unique_renderer_id == predicted_id; - }); - if (iter != possible_usernames.end()) { - return *iter; - } - } - return nullptr; -} - // Extracts the username predictions. |control_elements| should be all the DOM // elements of the form, |form_data| should be the already extracted FormData // representation of that form. |username_detector_cache| is optional, and can @@ -401,387 +65,6 @@ std::vector<uint32_t> GetUsernamePredictions( username_detector_cache); } -// Get information about a login form encapsulated in a PasswordForm struct. -// If an element of |form| has an entry in |nonscript_modified_values|, the -// associated string is used instead of the element's value to create -// the PasswordForm. -bool GetPasswordForm(const GURL& form_origin, - const std::vector<WebFormControlElement>& control_elements, - PasswordForm* password_form, - const FormsPredictionsMap* form_predictions, - UsernameDetectorCache* username_detector_cache) { - DCHECK(!control_elements.empty()); - - const FormData& form_data = password_form->form_data; - PasswordContents password_contents = GetPasswordContents(form_data.fields); - switch (password_contents) { - case PasswordContents::kEnabled: - // All well, continue parsing. - break; - case PasswordContents::kOnlyDisabled: - // The current parser gives up, but returns a fallback form so that the - // newer parser can try parsing as well. - password_form->scheme = PasswordForm::Scheme::kHtml; - password_form->origin = form_origin; - password_form->signon_realm = GetSignOnRealm(password_form->origin); - return true; - case PasswordContents::kNone: - return false; - } - // Evaluate the context of the fields. - password_form->form_data.username_predictions = GetUsernamePredictions( - control_elements, form_data, username_detector_cache); - - // Narrow the scope to enabled text inputs. - std::vector<const FormFieldData*> enabled_fields; - enabled_fields.reserve(form_data.fields.size()); - for (const FormFieldData& field : form_data.fields) { - if (field.is_enabled && field.IsTextInputElement()) - enabled_fields.push_back(&field); - } - - // Remember the list of password fields without any heuristics applied in case - // the heuristics fail and a fall-back is needed: - // All password fields. - std::vector<const FormFieldData*> passwords_without_heuristics; - // Map from all password fields to the most recent non-password text input. - std::map<const FormFieldData*, const FormFieldData*> - preceding_text_input_for_password_without_heuristics; - const FormFieldData* most_recent_text_input = nullptr; // Just a temporary. - for (const FormFieldData* input : enabled_fields) { - if (input->form_control_type == "password") { - passwords_without_heuristics.push_back(input); - preceding_text_input_for_password_without_heuristics[input] = - most_recent_text_input; - } else { - most_recent_text_input = input; - } - } - - // Fill the cache with autocomplete flags. - AutocompleteCache autocomplete_cache; - for (const FormFieldData* input : enabled_fields) { - autocomplete_cache.Store(input); - } - - // Narrow the scope further: drop credit-card fields. - std::vector<const FormFieldData*> plausible_inputs; - plausible_inputs.reserve(enabled_fields.size()); - for (const FormFieldData* input : enabled_fields) { - const AutocompleteFlag flag = autocomplete_cache.RetrieveFor(input); - if (flag == AutocompleteFlag::CURRENT_PASSWORD || - flag == AutocompleteFlag::NEW_PASSWORD) { - // A field marked as a password is considered not a credit-card field, no - // matter what. - plausible_inputs.push_back(input); - } else if (flag != AutocompleteFlag::CREDIT_CARD) { - const bool is_credit_card_verification = - input->form_control_type == "password" && - (StringMatchesCVC(input->name_attribute) || - StringMatchesCVC(input->id_attribute)); - if (!is_credit_card_verification) { - // Otherwise ensure that nothing hints that |input| is a credit-card - // field. - plausible_inputs.push_back(input); - } - } - } - - // Further narrow to interesting fields (e.g., with user input, visible), if - // present. - // Compute the best filtering levels for usernames and for passwords. - FieldFilteringLevel username_fields_level = FieldFilteringLevel::NO_FILTER; - FieldFilteringLevel password_fields_level = FieldFilteringLevel::NO_FILTER; - GetFieldFilteringLevels(form_data.fields, &username_fields_level, - &password_fields_level); - // Remove all fields with filtering level below the best. - base::EraseIf( - plausible_inputs, [password_fields_level, - username_fields_level](const FormFieldData* input) { - FieldFilteringLevel current_field_level = GetFiltertingLevelForField( - *input, false /* ignore_autofilled_values */); - if (input->form_control_type == "password") - return current_field_level < password_fields_level; - return current_field_level < username_fields_level; - }); - - // Further, remove all readonly passwords. If the password field is readonly, - // the page is likely using a virtual keyboard and bypassing the password - // field value (see http://crbug.com/475488). There is nothing Chrome can do - // to fill passwords for now. Notable exceptions: if the password field was - // made readonly by JavaScript before submission, it remains interesting. If - // the password was marked via the autocomplete attribute, it also remains - // interesting. - base::EraseIf(plausible_inputs, [&autocomplete_cache]( - const FormFieldData* input) { - if (!input->is_readonly) - return false; - if (input->form_control_type != "password") - return false; - // Check if the field was only made readonly before submission. - if (input->properties_mask & - (FieldPropertiesFlags::USER_TYPED | FieldPropertiesFlags::AUTOFILLED)) { - return false; - } - // Check whether the field was explicitly marked as password. - const AutocompleteFlag flag = autocomplete_cache.RetrieveFor(input); - if (flag == AutocompleteFlag::CURRENT_PASSWORD || - flag == AutocompleteFlag::NEW_PASSWORD) { - return false; - } - return true; - }); - - // Evaluate available server-side predictions. - std::map<const FormFieldData*, PasswordFormFieldPredictionType> - predicted_fields; - const FormFieldData* predicted_username_field = nullptr; - if (form_predictions) { - FindPredictedElements(password_form->form_data, *form_predictions, - &predicted_fields); - - for (const auto& predicted_pair : predicted_fields) { - if (predicted_pair.second == PasswordFormFieldPredictionType::kUsername) { - predicted_username_field = predicted_pair.first; - break; - } - } - } - - // Finally, remove all password fields for which we have a negative - // prediction, unless they are explicitly marked by the autocomplete attribute - // as a password. - base::EraseIf(plausible_inputs, [&predicted_fields, &autocomplete_cache]( - const FormFieldData* input) { - if (input->form_control_type != "password") - return false; - const AutocompleteFlag flag = autocomplete_cache.RetrieveFor(input); - if (flag == AutocompleteFlag::CURRENT_PASSWORD || - flag == AutocompleteFlag::NEW_PASSWORD) { - return false; - } - auto possible_password_field_iterator = predicted_fields.find(input); - return possible_password_field_iterator != predicted_fields.end() && - possible_password_field_iterator->second == - PasswordFormFieldPredictionType::kNotPassword; - }); - - // Derive the list of all plausible passwords, usernames and the non-password - // inputs preceding the plausible passwords. - std::vector<const FormFieldData*> plausible_passwords; - std::vector<const FormFieldData*> plausible_usernames; - std::map<const FormFieldData*, const FormFieldData*> - preceding_text_input_for_plausible_password; - most_recent_text_input = nullptr; - plausible_usernames.reserve(plausible_inputs.size()); - for (const FormFieldData* input : plausible_inputs) { - if (input->form_control_type == "password") { - plausible_passwords.push_back(input); - preceding_text_input_for_plausible_password[input] = - most_recent_text_input; - } else { - plausible_usernames.push_back(input); - most_recent_text_input = input; - } - } - - // Evaluate autocomplete attributes for username. - const FormFieldData* username_by_attribute = nullptr; - for (const FormFieldData* input : plausible_inputs) { - if (input->form_control_type != "password") { - if (autocomplete_cache.RetrieveFor(input) == AutocompleteFlag::USERNAME) { - // Only consider the first occurrence of autocomplete='username'. - // Multiple occurences hint at the attribute being used incorrectly, in - // which case sticking to the first one is just a bet. - if (!username_by_attribute) { - username_by_attribute = input; - break; - } - } - } - } - - // Evaluate the context of the fields. - const FormFieldData* username_field_by_context = nullptr; - // Use HTML based username detector only if neither server predictions nor - // autocomplete attributes were useful to detect the username. - if (!predicted_username_field && !username_by_attribute) { - username_field_by_context = FindUsernameInPredictions( - form_data.username_predictions, plausible_usernames); - } - - // Populate all_possible_passwords and form_has_autofilled_value in - // |password_form|. - // Contains the first password element for each non-empty password value. - std::vector<ValueElementPair> all_possible_passwords; - // Reserve enough space to prevent re-allocation. A re-allocation would - // invalidate the contents of |seen_values|. - all_possible_passwords.reserve(passwords_without_heuristics.size()); - std::set<base::StringPiece16> seen_values; - // Pretend that an empty value has been already seen, so that empty-valued - // password elements won't get added to |all_possible_passwords|. - seen_values.insert(base::StringPiece16()); - for (const FormFieldData* password_field : passwords_without_heuristics) { - if (seen_values.count(password_field->value) > 0) - continue; - all_possible_passwords.push_back( - {password_field->value, password_field->name}); - seen_values.insert( - base::StringPiece16(all_possible_passwords.back().first)); - } - - bool form_has_autofilled_value = false; - for (const FormFieldData* password_field : passwords_without_heuristics) { - bool field_has_autofilled_value = - password_field->properties_mask & FieldPropertiesFlags::AUTOFILLED; - form_has_autofilled_value |= field_has_autofilled_value; - } - - if (!all_possible_passwords.empty()) { - password_form->all_possible_passwords = std::move(all_possible_passwords); - password_form->form_has_autofilled_value = form_has_autofilled_value; - } - - // If for some reason (e.g. only credit card fields, confusing autocomplete - // attributes) the passwords list is empty, build list based on user input (if - // there is any non-empty password field) and the type of a field. Also mark - // that the form should be available only for fallback saving (automatic - // bubble will not pop up) and filling. - password_form->only_for_fallback = plausible_passwords.empty(); - if (plausible_passwords.empty()) { - plausible_passwords = std::move(passwords_without_heuristics); - preceding_text_input_for_plausible_password = - std::move(preceding_text_input_for_password_without_heuristics); - } - - // Find the password fields. - const FormFieldData* password = nullptr; - const FormFieldData* new_password = nullptr; - const FormFieldData* confirmation_password = nullptr; - LocateSpecificPasswords(std::move(plausible_passwords), &password, - &new_password, &confirmation_password, - autocomplete_cache); - - // Choose the username element. - const FormFieldData* username_field = nullptr; - UsernameDetectionMethod username_detection_method = - UsernameDetectionMethod::NO_USERNAME_DETECTED; - password_form->username_marked_by_site = false; - - if (predicted_username_field) { - // Server predictions are most trusted, so try them first. Only if the form - // already has user input and the predicted username field has an empty - // value, then don't trust the prediction (can be caused by, e.g., a <form> - // actually contains several forms). - if (password_fields_level < FieldFilteringLevel::USER_INPUT || - !predicted_username_field->value.empty()) { - username_field = predicted_username_field; - password_form->was_parsed_using_autofill_predictions = true; - username_detection_method = - UsernameDetectionMethod::SERVER_SIDE_PREDICTION; - } - } - - if (!username_field && username_by_attribute) { - // Next in the trusted queue: autocomplete attributes. - username_field = username_by_attribute; - username_detection_method = UsernameDetectionMethod::AUTOCOMPLETE_ATTRIBUTE; - } - - if (!username_field && username_field_by_context) { - // Last step before base heuristics: HTML-based classifier. - username_field = username_field_by_context; - username_detection_method = UsernameDetectionMethod::HTML_BASED_CLASSIFIER; - } - - // Compute base heuristic for username detection. - const FormFieldData* base_heuristic_username = nullptr; - if (password) { - base_heuristic_username = - preceding_text_input_for_plausible_password[password]; - } - if (!base_heuristic_username && new_password) { - base_heuristic_username = - preceding_text_input_for_plausible_password[new_password]; - } - - // Apply base heuristic for username detection. - if (!username_field) { - username_field = base_heuristic_username; - if (username_field) - username_detection_method = UsernameDetectionMethod::BASE_HEURISTIC; - } else if (base_heuristic_username == username_field && - username_detection_method != - UsernameDetectionMethod::AUTOCOMPLETE_ATTRIBUTE) { - // TODO(crbug.com/786404): when the bug is fixed, remove this block and - // calculate |base_heuristic_username| only if |username_field| is null. - // This block was added to measure the impact of server-side predictions and - // HTML based classifier compared to "old classifiers" (the based heuristic - // and 'autocomplete' attribute). - username_detection_method = UsernameDetectionMethod::BASE_HEURISTIC; - } - UMA_HISTOGRAM_ENUMERATION( - "PasswordManager.UsernameDetectionMethod", username_detection_method, - UsernameDetectionMethod::USERNAME_DETECTION_METHOD_COUNT); - - // Populate the username fields in |password_form|. - if (username_field) { - password_form->username_element = - FieldName(username_field, "anonymous_username"); - password_form->username_value = username_field->value; - if ((username_field->properties_mask & - (FieldPropertiesFlags::USER_TYPED | - FieldPropertiesFlags::AUTOFILLED)) && - !username_field->typed_value.empty()) { - password_form->username_value = username_field->typed_value; - } - } - - // Populate the password fields in |password_form|. - if (password) { - password_form->password_element = FieldName(password, "anonymous_password"); - password_form->password_value = password->value; - if ((password->properties_mask & (FieldPropertiesFlags::USER_TYPED | - FieldPropertiesFlags::AUTOFILLED)) && - !password->typed_value.empty()) { - password_form->password_value = password->typed_value; - } - } - if (new_password) { - password_form->new_password_element = - FieldName(new_password, "anonymous_new_password"); - password_form->new_password_value = new_password->value; - if (autocomplete_cache.RetrieveFor(new_password) == - AutocompleteFlag::NEW_PASSWORD) { - password_form->new_password_marked_by_site = true; - } - if (confirmation_password) { - password_form->confirmation_password_element = - FieldName(confirmation_password, "anonymous_confirmation_password"); - } - } - - // Populate |all_possible_usernames| in |password_form|. - ValueElementVector all_possible_usernames; - for (const FormFieldData* plausible_username : plausible_usernames) { - if (plausible_username == username_field) - continue; - auto pair = MakePossibleUsernamePair(plausible_username); - if (!pair.first.empty()) - all_possible_usernames.push_back(std::move(pair)); - } - password_form->all_possible_usernames = std::move(all_possible_usernames); - - password_form->origin = std::move(form_origin); - password_form->signon_realm = GetSignOnRealm(password_form->origin); - password_form->scheme = PasswordForm::Scheme::kHtml; - password_form->preferred = false; - password_form->blacklisted_by_user = false; - password_form->type = PasswordForm::Type::kManual; - - return true; -} - bool HasGaiaSchemeAndHost(const WebFormElement& form) { GURL form_url = form.GetDocument().Url(); GURL gaia_url = GaiaUrls::GetInstance()->gaia_url(); @@ -791,13 +74,6 @@ bool HasGaiaSchemeAndHost(const WebFormElement& form) { } // namespace -AutocompleteFlag AutocompleteFlagForElement(const WebInputElement& element) { - static const base::NoDestructor<WebString> kAutocomplete(("autocomplete")); - return ExtractAutocompleteFlag( - element.GetAttribute(*kAutocomplete) - .Utf8(WebString::UTF8ConversionMode::kStrictReplacingErrorsWithFFFD)); -} - re2::RE2* CreateMatcher(void* instance, const char* pattern) { re2::RE2::Options options; options.set_case_sensitive(false); @@ -854,7 +130,8 @@ bool IsGaiaWithSkipSavePasswordForm(const blink::WebFormElement& form) { std::unique_ptr<PasswordForm> CreateSimplifiedPasswordFormFromWebForm( const WebFormElement& web_form, - const FieldDataManager* field_data_manager) { + const FieldDataManager* field_data_manager, + UsernameDetectorCache* username_detector_cache) { if (web_form.IsNull()) return nullptr; @@ -867,32 +144,6 @@ std::unique_ptr<PasswordForm> CreateSimplifiedPasswordFormFromWebForm( IsGaiaWithSkipSavePasswordForm(web_form) || IsGaiaReauthenticationForm(web_form); - if (!WebFormElementToFormData(web_form, WebFormControlElement(), - field_data_manager, form_util::EXTRACT_VALUE, - &password_form->form_data, - nullptr /* FormFieldData */)) { - return nullptr; - } - - return password_form; -} - -std::unique_ptr<PasswordForm> CreatePasswordFormFromWebForm( - const WebFormElement& web_form, - const FieldDataManager* field_data_manager, - const FormsPredictionsMap* form_predictions, - UsernameDetectorCache* username_detector_cache) { - if (web_form.IsNull()) - return nullptr; - - auto password_form = std::make_unique<PasswordForm>(); - password_form->action = form_util::GetCanonicalActionForForm(web_form); - if (!password_form->action.is_valid()) - return nullptr; - password_form->form_data.is_gaia_with_skip_save_password_form = - IsGaiaWithSkipSavePasswordForm(web_form) || - IsGaiaReauthenticationForm(web_form); - blink::WebVector<WebFormControlElement> control_elements; web_form.GetFormControlElements(control_elements); if (control_elements.empty()) @@ -904,20 +155,18 @@ std::unique_ptr<PasswordForm> CreatePasswordFormFromWebForm( nullptr /* FormFieldData */)) { return nullptr; } + password_form->form_data.username_predictions = + GetUsernamePredictions(control_elements.ReleaseVector(), + password_form->form_data, username_detector_cache); - if (!GetPasswordForm( - form_util::GetCanonicalOriginForDocument(web_form.GetDocument()), - control_elements.ReleaseVector(), password_form.get(), - form_predictions, username_detector_cache)) { - return nullptr; - } return password_form; } std::unique_ptr<PasswordForm> CreateSimplifiedPasswordFormFromUnownedInputElements( const WebLocalFrame& frame, - const FieldDataManager* field_data_manager) { + const FieldDataManager* field_data_manager, + UsernameDetectorCache* username_detector_cache) { std::vector<WebElement> fieldsets; std::vector<WebFormControlElement> control_elements = form_util::GetUnownedFormFieldElements(frame.GetDocument().All(), @@ -936,38 +185,8 @@ CreateSimplifiedPasswordFormFromUnownedInputElements( password_form->origin = form_util::GetCanonicalOriginForDocument(frame.GetDocument()); password_form->signon_realm = GetSignOnRealm(password_form->origin); - return password_form; -} - -std::unique_ptr<PasswordForm> CreatePasswordFormFromUnownedInputElements( - const WebLocalFrame& frame, - const FieldDataManager* field_data_manager, - const FormsPredictionsMap* form_predictions, - UsernameDetectorCache* username_detector_cache) { - std::vector<WebElement> fieldsets; - std::vector<WebFormControlElement> control_elements = - form_util::GetUnownedFormFieldElements(frame.GetDocument().All(), - &fieldsets); - if (control_elements.empty()) - return nullptr; - - auto password_form = std::make_unique<PasswordForm>(); - if (!UnownedPasswordFormElementsAndFieldSetsToFormData( - fieldsets, control_elements, nullptr, frame.GetDocument(), - field_data_manager, form_util::EXTRACT_VALUE, - &password_form->form_data, nullptr /* FormFieldData */)) { - return nullptr; - } - - if (!GetPasswordForm( - form_util::GetCanonicalOriginForDocument(frame.GetDocument()), - control_elements, password_form.get(), form_predictions, - username_detector_cache)) { - return nullptr; - } - - // No actual action on the form, so use the the origin as the action. - password_form->action = password_form->origin; + password_form->form_data.username_predictions = GetUsernamePredictions( + control_elements, password_form->form_data, username_detector_cache); return password_form; } 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 e02034c893b..3674d438d72 100644 --- a/chromium/components/autofill/content/renderer/password_form_conversion_utils.h +++ b/chromium/components/autofill/content/renderer/password_form_conversion_utils.h @@ -14,13 +14,11 @@ #include "base/strings/string_piece.h" #include "components/autofill/content/renderer/html_based_username_detector.h" #include "components/autofill/core/common/password_form.h" -#include "components/autofill/core/common/password_form_field_prediction_map.h" #include "third_party/blink/public/platform/web_string.h" #include "url/gurl.h" namespace blink { class WebFormElement; -class WebInputElement; class WebLocalFrame; } @@ -31,32 +29,8 @@ class RE2; namespace autofill { struct PasswordForm; - class FieldDataManager; -enum UsernameDetectionMethod { - NO_USERNAME_DETECTED, - BASE_HEURISTIC, - HTML_BASED_CLASSIFIER, - AUTOCOMPLETE_ATTRIBUTE, - SERVER_SIDE_PREDICTION, - USERNAME_DETECTION_METHOD_COUNT -}; - -// The susbset of autocomplete flags related to passwords. -enum class AutocompleteFlag { - NONE, - USERNAME, - CURRENT_PASSWORD, - NEW_PASSWORD, - // Represents the whole family of cc-* flags. - CREDIT_CARD -}; - -// Returns the AutocompleteFlag derived from |element|'s autocomplete attribute. -AutocompleteFlag AutocompleteFlagForElement( - const blink::WebInputElement& element); - // The caller of this function is responsible for deleting the returned object. re2::RE2* CreateMatcher(void* instance, const char* pattern); @@ -66,34 +40,11 @@ bool IsGaiaReauthenticationForm(const blink::WebFormElement& form); // Tests whether the given form is a GAIA form with a skip password argument. bool IsGaiaWithSkipSavePasswordForm(const blink::WebFormElement& form); -// Create a PasswordForm from DOM form. Webkit doesn't allow storing -// custom metadata to DOM nodes, so we have to do this every time an event -// happens with a given form and compare against previously Create'd forms -// to identify..which sucks. -// If an element of |form| has an entry in |field_data_manager|, the associated -// string is used instead of the element's value to create the PasswordForm. -// |form_predictions| is Autofill server response, if present it's used for -// overwriting default username element selection. -// |username_detector_cache| is used by the built-in HTML based username -// detector to cache results. Can be null. -std::unique_ptr<PasswordForm> CreatePasswordFormFromWebForm( - const blink::WebFormElement& form, - const FieldDataManager* field_data_manager, - const FormsPredictionsMap* form_predictions, - UsernameDetectorCache* username_detector_cache); - // Creates a |PasswordForm| from DOM which only contains the |form_data| as well // as origin, action and gaia flags. std::unique_ptr<PasswordForm> CreateSimplifiedPasswordFormFromWebForm( const blink::WebFormElement& form, - const FieldDataManager* field_data_manager); - -// Same as CreatePasswordFormFromWebForm() but for input elements that are not -// enclosed in <form> element. -std::unique_ptr<PasswordForm> CreatePasswordFormFromUnownedInputElements( - const blink::WebLocalFrame& frame, const FieldDataManager* field_data_manager, - const FormsPredictionsMap* form_predictions, UsernameDetectorCache* username_detector_cache); // Same as CreateSimlePasswordFormFromWebForm() but for input elements that are @@ -101,7 +52,8 @@ std::unique_ptr<PasswordForm> CreatePasswordFormFromUnownedInputElements( std::unique_ptr<PasswordForm> CreateSimplifiedPasswordFormFromUnownedInputElements( const blink::WebLocalFrame& frame, - const FieldDataManager* field_data_manager); + const FieldDataManager* field_data_manager, + UsernameDetectorCache* username_detector_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_form_conversion_utils_browsertest.cc b/chromium/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc index 02b36b862d4..7b67bc51d62 100644 --- a/chromium/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc +++ b/chromium/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc @@ -5,47 +5,24 @@ #include <stddef.h> #include <memory> -#include "base/stl_util.h" -#include "base/strings/string16.h" -#include "base/strings/string_util.h" #include "base/strings/stringprintf.h" -#include "base/strings/utf_string_conversions.h" -#include "base/test/metrics/histogram_tester.h" -#include "base/test/scoped_feature_list.h" -#include "build/build_config.h" -#include "components/autofill/content/renderer/field_data_manager.h" -#include "components/autofill/content/renderer/form_autofill_util.h" #include "components/autofill/content/renderer/password_form_conversion_utils.h" -#include "components/autofill/core/browser/form_structure.h" -#include "components/autofill/core/common/form_field_data.h" -#include "components/autofill/core/common/password_form.h" #include "content/public/test/render_view_test.h" #include "google_apis/gaia/gaia_urls.h" -#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/platform/web_vector.h" #include "third_party/blink/public/web/web_document.h" -#include "third_party/blink/public/web/web_form_control_element.h" #include "third_party/blink/public/web/web_form_element.h" -#include "third_party/blink/public/web/web_input_element.h" #include "third_party/blink/public/web/web_local_frame.h" -using blink::WebElement; -using blink::WebFormControlElement; using blink::WebFormElement; -using blink::WebInputElement; using blink::WebLocalFrame; -using blink::WebString; using blink::WebVector; namespace autofill { -using mojom::PasswordFormFieldPredictionType; - namespace { -const char kTestFormActionURL[] = "http://cnn.com"; - // A builder to produce HTML code for a password form composed of the desired // number and kinds of username and password fields. class PasswordFormBuilder { @@ -73,20 +50,6 @@ class PasswordFormBuilder { name_and_id, name_and_id, value, autocomplete_attribute.c_str()); } - // Add a text field with name, id, value, label and placeholder, - // without autocomplete. - void AddTextFieldWithoutAutocomplete(const char* name, - const char* id, - const char* value, - const char* label, - const char* placeholder) { - base::StringAppendF(&html_, - "<LABEL for=\"%s\">%s</LABEL>" - "<INPUT type=\"text\" name=\"%s\" id=\"%s\" " - "value=\"%s\" placeholder=\"%s\"/>", - id, label, name, id, value, placeholder); - } - // Appends a new password-type field at the end of the form, having the // specified |name_and_id|, |value|, and |autocomplete| attributes. Special // values for |autocomplete| are the same as in AddTextField. @@ -102,19 +65,6 @@ class PasswordFormBuilder { name_and_id, name_and_id, value, autocomplete_attribute.c_str()); } - // Appends a disabled text-type field at the end of the form. - void AddDisabledUsernameField() { - html_ += "<INPUT name=\"disabled field\" type=\"text\" disabled/>"; - } - - // Appends a disabled password-type field at the end of the form. - void AddDisabledPasswordField() { - html_ += "<INPUT name=\"disabled field\" type=\"password\" disabled/>"; - } - - // Appends a hidden field at the end of the form. - void AddHiddenField() { html_ += "<INPUT type=\"hidden\"/>"; } - // Appends a new hidden-type field at the end of the form, having the // specified |name_and_id| and |value| attributes. void AddHiddenField(const char* name_and_id, const char* value) { @@ -123,68 +73,10 @@ class PasswordFormBuilder { name_and_id, name_and_id, value); } - // Append a text field with "display: none". - void AddNonDisplayedTextField(const char* name_and_id, const char* value) { - base::StringAppendF( - &html_, - "<INPUT type=\"text\" name=\"%s\" id=\"%s\" value=\"%s\"" - "style=\"display: none;\"/>", - name_and_id, name_and_id, value); - } - - // Append a password field with "display: none". - void AddNonDisplayedPasswordField(const char* name_and_id, - const char* value) { - base::StringAppendF( - &html_, - "<INPUT type=\"password\" name=\"%s\" id=\"%s\" value=\"%s\"" - "style=\"display: none;\"/>", - name_and_id, name_and_id, value); - } - - // Append a text field with "visibility: hidden". - void AddNonVisibleTextField(const char* name_and_id, const char* value) { - base::StringAppendF( - &html_, - "<INPUT type=\"text\" name=\"%s\" id=\"%s\" value=\"%s\"" - "style=\"visibility: hidden;\"/>", - name_and_id, name_and_id, value); - } - - // Append a password field with "visibility: hidden". - void AddNonVisiblePasswordField(const char* name_and_id, const char* value) { - base::StringAppendF( - &html_, - "<INPUT type=\"password\" name=\"%s\" id=\"%s\" value=\"%s\"" - "style=\"visibility: hidden;\"/>", - name_and_id, name_and_id, value); - } - - // Add a field with a given type. Useful to add non-text fields. - void AddFieldWithType(const char* name_and_id, const char* type) { - base::StringAppendF(&html_, "<INPUT type=\"%s\" name=\"%s\" id=\"%s\"/>", - type, name_and_id, name_and_id); - } - - // Appends a new submit-type field at the end of the form with the specified - // |name|. - void AddSubmitButton(const char* name) { - base::StringAppendF( - &html_, "<INPUT type=\"submit\" name=\"%s\" value=\"Submit\"/>", name); - } - // Returns the HTML code for the form containing the fields that have been // added so far. std::string ProduceHTML() const { return html_ + "</FORM>"; } - // Appends a field of |type| without name or id attribute at the end of the - // form. - void AddAnonymousInputField(const char* type) { - std::string type_attribute(type ? base::StringPrintf("type=\"%s\"", type) - : ""); - base::StringAppendF(&html_, "<INPUT %s/>", type_attribute.c_str()); - } - private: std::string html_; @@ -197,74 +89,6 @@ class PasswordFormConversionUtilsTest : public content::RenderViewTest { ~PasswordFormConversionUtilsTest() override = default; protected: - // Loads the given |html|, retrieves the sole WebFormElement from it, and then - // calls CreatePasswordForm(), passing it the |predictions| to convert it to - // a PasswordForm. If |with_user_input| == true it's considered that all - // values in the form elements came from the user input. - std::unique_ptr<PasswordForm> LoadHTMLAndConvertForm( - const std::string& html, - FormsPredictionsMap* predictions, - bool with_user_input) { - WebFormElement form; - LoadWebFormFromHTML(html, &form, nullptr); - - WebVector<WebFormControlElement> control_elements; - form.GetFormControlElements(control_elements); - FieldDataManager field_data_manager; - for (size_t i = 0; i < control_elements.size(); ++i) { - WebInputElement* input_element = ToWebInputElement(&control_elements[i]); - if (input_element->HasAttribute("set-activated-submit")) - input_element->SetActivatedSubmit(true); - if (with_user_input) { - field_data_manager.UpdateFieldDataMap(control_elements[i], - input_element->Value().Utf16(), - FieldPropertiesFlags::USER_TYPED); - } - } - - return CreatePasswordFormFromWebForm( - form, with_user_input ? &field_data_manager : nullptr, predictions, - &username_detector_cache_); - } - - // Iterates on the form generated by the |html| and adds the fields and type - // predictions corresponding to |predictions_positions| to |predictions|. - void SetPredictions(const std::string& html, - FormsPredictionsMap* predictions, - const std::map<int, PasswordFormFieldPredictionType>& - predictions_positions) { - WebFormElement form; - LoadWebFormFromHTML(html, &form, nullptr); - - FormData form_data; - ASSERT_TRUE(form_util::WebFormElementToFormData( - form, WebFormControlElement(), nullptr, form_util::EXTRACT_NONE, - &form_data, nullptr)); - - FormStructure form_structure(form_data); - - int field_index = 0; - for (auto field = form_structure.begin(); field != form_structure.end(); - ++field, ++field_index) { - if (predictions_positions.find(field_index) != - predictions_positions.end()) { - (*predictions)[form_data][*(*field)] = - predictions_positions.find(field_index)->second; - } - } - } - - void GetFirstForm(WebFormElement* form) { - WebLocalFrame* frame = GetMainFrame(); - ASSERT_TRUE(frame); - - WebVector<WebFormElement> forms; - frame->GetDocument().Forms(forms); - ASSERT_LE(1U, forms.size()); - - *form = forms[0]; - } - // Loads the given |html| and retrieves the sole WebFormElement from it. void LoadWebFormFromHTML(const std::string& html, WebFormElement* form, @@ -277,1799 +101,27 @@ class PasswordFormConversionUtilsTest : public content::RenderViewTest { GetFirstForm(form); } - bool ExtractFormDataForFirstForm(FormData* data) { - WebFormElement form; - GetFirstForm(&form); - return form_util::ExtractFormData(form, data); - } - void TearDown() override { - username_detector_cache_.clear(); content::RenderViewTest::TearDown(); } - uint32_t GetRendererIdFromWebElementId(WebString id) { + private: + void GetFirstForm(WebFormElement* form) { WebLocalFrame* frame = GetMainFrame(); - if (!frame || frame->GetDocument().IsNull()) - return FormData::kNotSetFormRendererId; - WebElement element = frame->GetDocument().GetElementById(id); - if (element.IsNull()) - return FormData::kNotSetFormRendererId; - return element.To<WebInputElement>().UniqueRendererFormControlId(); - } + ASSERT_TRUE(frame); - UsernameDetectorCache username_detector_cache_; + WebVector<WebFormElement> forms; + frame->GetDocument().Forms(forms); + ASSERT_LE(1U, forms.size()); + + *form = forms[0]; + } - private: DISALLOW_COPY_AND_ASSIGN(PasswordFormConversionUtilsTest); }; } // namespace -TEST_F(PasswordFormConversionUtilsTest, BasicFormAttributes) { - PasswordFormBuilder builder(kTestFormActionURL); - builder.AddTextField("username", "johnsmith", nullptr); - builder.AddSubmitButton("inactive_submit"); - builder.AddSubmitButton("active_submit"); - builder.AddSubmitButton("inactive_submit2"); - builder.AddPasswordField("password", "secret", nullptr); - std::string html = builder.ProduceHTML(); - - std::unique_ptr<PasswordForm> password_form = - LoadHTMLAndConvertForm(html, nullptr, false); - ASSERT_TRUE(password_form); - - EXPECT_FALSE(password_form->only_for_fallback); - EXPECT_EQ("data:", password_form->signon_realm); - EXPECT_EQ(GURL(kTestFormActionURL), password_form->action); - EXPECT_EQ(base::UTF8ToUTF16("username"), password_form->username_element); - EXPECT_EQ(base::UTF8ToUTF16("johnsmith"), password_form->username_value); - EXPECT_EQ(base::UTF8ToUTF16("password"), password_form->password_element); - EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->password_value); - EXPECT_EQ(PasswordForm::Scheme::kHtml, password_form->scheme); - EXPECT_FALSE(password_form->preferred); - EXPECT_FALSE(password_form->blacklisted_by_user); - EXPECT_EQ(PasswordForm::Type::kManual, password_form->type); -} - -TEST_F(PasswordFormConversionUtilsTest, DisabledFieldsAreIgnored) { - PasswordFormBuilder builder(kTestFormActionURL); - builder.AddTextField("username", "johnsmith", nullptr); - builder.AddDisabledUsernameField(); - builder.AddDisabledPasswordField(); - builder.AddPasswordField("password", "secret", nullptr); - builder.AddSubmitButton("submit"); - std::string html = builder.ProduceHTML(); - - std::unique_ptr<PasswordForm> password_form = - LoadHTMLAndConvertForm(html, nullptr, false); - ASSERT_TRUE(password_form); - EXPECT_FALSE(password_form->only_for_fallback); - EXPECT_EQ(base::UTF8ToUTF16("username"), password_form->username_element); - EXPECT_EQ(base::UTF8ToUTF16("johnsmith"), password_form->username_value); - EXPECT_EQ(base::UTF8ToUTF16("password"), password_form->password_element); - EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->password_value); -} - -// When not enough fields are enabled to parse the form, the result should still -// be not null. It must contain only minimal information, so that it is not used -// for fill on load, for example. It must contain the full FormData, so that the -// new parser can be run as well. -TEST_F(PasswordFormConversionUtilsTest, OnlyDisabledFields) { - PasswordFormBuilder builder(kTestFormActionURL); - builder.AddDisabledUsernameField(); - builder.AddDisabledPasswordField(); - builder.AddSubmitButton("submit"); - std::string html = builder.ProduceHTML(); - - std::unique_ptr<PasswordForm> password_form = - LoadHTMLAndConvertForm(html, nullptr, false); - ASSERT_TRUE(password_form); - EXPECT_TRUE(password_form->username_element.empty()); - EXPECT_TRUE(password_form->password_element.empty()); - EXPECT_TRUE(password_form->new_password_element.empty()); - EXPECT_EQ(2u, password_form->form_data.fields.size()); -} - -TEST_F(PasswordFormConversionUtilsTest, HTMLDetector_DeveloperGroupAttributes) { - // Each test case consists of a set of parameters to be plugged into the - // PasswordFormBuilder below, 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). - struct TestCase { - // Field parameters represent, in order of appearance, field name, field id - // and field value. - const char* first_text_field_parameters[3]; - const char* second_text_field_parameters[3]; - const char* expected_username_element; - const WebString expected_username_id; - const char* expected_username_value; - } cases[] = { - // There are both field name and id. - {{"username", "x1d", "johnsmith"}, - {"email", "y1d", "js@google.com"}, - "username", - "x1d", - "johnsmith"}, - // there is no field id. - {{"username", "x1d", "johnsmith"}, - {"email", "y1d", "js@google.com"}, - "username", - "x1d", - "johnsmith"}, - // Upper or mixed case shouldn't matter. - {{"uSeRnAmE", "x1d", "johnsmith"}, - {"email", "y1d", "js@google.com"}, - "uSeRnAmE", - "x1d", - "johnsmith"}, - // Check removal of special characters. - {{"u1_s2-e3~r4/n5(a)6m#e", "x1d", "johnsmith"}, - {"email", "y1d", "js@google.com"}, - "u1_s2-e3~r4/n5(a)6m#e", - "x1d", - "johnsmith"}, - // Check guard between field name and field id. - {{"us", "ername", "johnsmith"}, - {"email", "id", "js@google.com"}, - "email", - "id", - "js@google.com"}, - // Check removal of fields with latin negative words in developer group. - {{"email", "x", "js@google.com"}, - {"fake_username", "y", "johnsmith"}, - "email", - "x", - "js@google.com"}, - {{"email", "mail", "js@google.com"}, - {"user_name", "fullname", "johnsmith"}, - "email", - "mail", - "js@google.com"}, - // Identify latin translations of "username". - {{"benutzername", "x", "johnsmith"}, - {"email", "y", "js@google.com"}, - "benutzername", - "x", - "johnsmith"}, - // Identify latin translations of "user". - {{"utilizator", "x1d", "johnsmith"}, - {"email", "y1d", "js@google.com"}, - "utilizator", - "x1d", - "johnsmith"}, - // Identify technical words. - {{"loginid", "x1d", "johnsmith"}, - {"email", "y1d", "js@google.com"}, - "loginid", - "x1d", - "johnsmith"}, - // Identify weak words. - {{"usrname", "x1d", "johnsmith"}, - {"email", "y1d", "js@google.com"}, - "email", - "y1d", - "js@google.com"}, - // If a word matches in maximum 2 fields, it is accepted. - // First encounter is selected as username. - {{"username", "x1d", "johnsmith"}, - {"repeat_username", "y1d", "johnsmith"}, - "username", - "x1d", - "johnsmith"}, - // A short word should be enclosed between delimiters. Otherwise, an - // Occurrence doesn't count. - {{"identity_name", "idn", "johnsmith"}, - {"id", "xid", "123"}, - "id", - "xid", - "123"}}; - - for (size_t i = 0; i < base::size(cases); ++i) { - SCOPED_TRACE(testing::Message() << "Iteration " << i); - - PasswordFormBuilder builder(kTestFormActionURL); - builder.AddTextFieldWithoutAutocomplete( - cases[i].first_text_field_parameters[0], - cases[i].first_text_field_parameters[1], - cases[i].first_text_field_parameters[2], "", ""); - builder.AddTextFieldWithoutAutocomplete( - cases[i].second_text_field_parameters[0], - cases[i].second_text_field_parameters[1], - cases[i].second_text_field_parameters[2], "", ""); - builder.AddPasswordField("password", "secret", nullptr); - builder.AddSubmitButton("submit"); - std::string html = builder.ProduceHTML(); - - username_detector_cache_.clear(); - - std::unique_ptr<PasswordForm> password_form = - LoadHTMLAndConvertForm(html, nullptr, false); - - uint32_t username_renderer_id = - GetRendererIdFromWebElementId(cases[i].expected_username_id); - - EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_username_element), - password_form->username_element); - EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_username_value), - password_form->username_value); - // Check that the username field was found by HTML detector. - ASSERT_EQ(1u, username_detector_cache_.size()); - ASSERT_FALSE(username_detector_cache_.begin()->second.empty()); - EXPECT_EQ(username_renderer_id, - username_detector_cache_.begin()->second[0]); - } -} - -TEST_F(PasswordFormConversionUtilsTest, HTMLDetector_SeveralDetections) { - // If word matches in more than 2 fields, we don't match on it. - // We search for match with another word. - PasswordFormBuilder builder(kTestFormActionURL); - builder.AddTextFieldWithoutAutocomplete("address", "xuser", "someaddress", "", - ""); - builder.AddTextFieldWithoutAutocomplete("loginid", "yuser", "johnsmith", "", - ""); - builder.AddTextFieldWithoutAutocomplete("tel", "zuser", "sometel", "", ""); - builder.AddPasswordField("password", "secret", nullptr); - builder.AddSubmitButton("submit"); - std::string html = builder.ProduceHTML(); - - DCHECK(username_detector_cache_.empty()); - std::unique_ptr<PasswordForm> password_form = - LoadHTMLAndConvertForm(html, nullptr, false); - - uint32_t username_renderer_id = GetRendererIdFromWebElementId("yuser"); - - ASSERT_TRUE(password_form); - - EXPECT_EQ(base::UTF8ToUTF16("loginid"), password_form->username_element); - EXPECT_EQ(base::UTF8ToUTF16("johnsmith"), password_form->username_value); - // Check that the username field was found by HTML detector. - ASSERT_EQ(1u, username_detector_cache_.size()); - ASSERT_EQ(1u, username_detector_cache_.begin()->second.size()); - EXPECT_EQ(username_renderer_id, username_detector_cache_.begin()->second[0]); -} - -TEST_F(PasswordFormConversionUtilsTest, HTMLDetector_UserGroupAttributes) { - // Each test case consists of a set of parameters to be plugged into the - // PasswordFormBuilder below, 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. - struct TestCase { - // Field parameters represent, in order of appearance, field name, field - // id, field value and field label or placeholder. - // Field name and field id don't contain any significant information. - const char* first_text_field_parameters[4]; - const char* second_text_field_parameters[4]; - const char* expected_username_element; - const WebString expected_username_id; - const char* expected_username_value; - } cases[] = { - // Label information will decide username. - {{"name1", "id1", "johnsmith", "Username:"}, - {"name2", "id2", "js@google.com", "Email:"}, - "name1", - "id1", - "johnsmith"}, - // Placeholder information will decide username. - {{"name1", "id1", "js@google.com", "Email:"}, - {"name2", "id2", "johnsmith", "Username:"}, - "name2", - "id2", - "johnsmith"}, - // Check removal of special characters. - {{"name1", "id1", "johnsmith", "U s er n a m e:"}, - {"name2", "id2", "js@google.com", "Email:"}, - "name1", - "id1", - "johnsmith"}, - // Check removal of fields with latin negative words in user group. - {{"name1", "id1", "johnsmith", "Username password:"}, - {"name2", "id2", "js@google.com", "Email:"}, - "name2", - "id2", - "js@google.com"}, - // Check removal of fields with non-latin negative words in user group. - {{"name1", "id1", "js@google.com", "Email:"}, - {"name2", "id2", "johnsmith", "የይለፍቃልየይለፍቃል:"}, - "name1", - "id1", - "js@google.com"}, - // Identify latin translations of "username". - {{"name1", "id1", "johnsmith", "Username:"}, - {"name2", "id2", "js@google.com", "Email:"}, - "name1", - "id1", - "johnsmith"}, - // Identify non-latin translations of "username". - {{"name1", "id1", "johnsmith", "用户名:"}, - {"name2", "id2", "js@google.com", "Email:"}, - "name1", - "id1", - "johnsmith"}, - // Identify latin translations of "user". - {{"name1", "id1", "johnsmith", "Wosuta:"}, - {"name2", "id2", "js@google.com", "Email:"}, - "name1", - "id1", - "johnsmith"}, - // Identify non-latin translations of "user". - {{"name1", "id1", "johnsmith", "истифода:"}, - {"name2", "id2", "js@google.com", "Email:"}, - "name1", - "id1", - "johnsmith"}, - // Identify weak words. - {{"name1", "id1", "johnsmith", "Insert your login details:"}, - {"name2", "id2", "js@google.com", "Insert your email:"}, - "name1", - "id1", - "johnsmith"}, - // 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:"}, - "email", - "id1", - "js@google.com"}, - // 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:"}, - "name1", - "noword", - "johnsmith"}}; - - for (size_t i = 0; i < base::size(cases); ++i) { - SCOPED_TRACE(testing::Message() << "Iteration " << i); - - PasswordFormBuilder builder(kTestFormActionURL); - builder.AddTextFieldWithoutAutocomplete( - cases[i].first_text_field_parameters[0], - cases[i].first_text_field_parameters[1], - cases[i].first_text_field_parameters[2], - cases[i].first_text_field_parameters[3], ""); - builder.AddTextFieldWithoutAutocomplete( - cases[i].second_text_field_parameters[0], - cases[i].second_text_field_parameters[1], - cases[i].second_text_field_parameters[2], "", - cases[i].second_text_field_parameters[3]); - builder.AddPasswordField("password", "secret", nullptr); - builder.AddSubmitButton("submit"); - std::string html = builder.ProduceHTML(); - - username_detector_cache_.clear(); - std::unique_ptr<PasswordForm> password_form = - LoadHTMLAndConvertForm(html, nullptr, false); - - uint32_t username_renderer_id = - GetRendererIdFromWebElementId(cases[i].expected_username_id); - - ASSERT_TRUE(password_form); - - EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_username_element), - password_form->username_element); - EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_username_value), - password_form->username_value); - // Check that the username field was found by HTML detector. - ASSERT_EQ(1u, username_detector_cache_.size()); - ASSERT_FALSE(username_detector_cache_.begin()->second.empty()); - EXPECT_EQ(username_renderer_id, - username_detector_cache_.begin()->second[0]); - } -} - -TEST_F(PasswordFormConversionUtilsTest, HTMLDetectorCache) { - PasswordFormBuilder builder(kTestFormActionURL); - builder.AddTextField("unknown", "12345", nullptr); - builder.AddTextField("something", "smith", nullptr); - builder.AddPasswordField("password", "secret", nullptr); - builder.AddSubmitButton("submit"); - std::string html = builder.ProduceHTML(); - WebFormElement form; - LoadWebFormFromHTML(html, &form, nullptr); - UsernameDetectorCache detector_cache; - - // No signals from HTML attributes. The classifier found nothing and cached - // it. - base::HistogramTester histogram_tester; - std::unique_ptr<PasswordForm> password_form = - CreatePasswordFormFromWebForm(form, nullptr, nullptr, &detector_cache); - - EXPECT_TRUE(password_form); - ASSERT_EQ(1u, detector_cache.size()); - EXPECT_EQ(form.UniqueRendererFormId(), detector_cache.begin()->first); - EXPECT_TRUE(detector_cache.begin()->second.empty()); - histogram_tester.ExpectUniqueSample("PasswordManager.UsernameDetectionMethod", - UsernameDetectionMethod::BASE_HEURISTIC, - 1); - - // Changing attributes would change the classifier's output. But the output - // will be the same because it was cached in |username_detector_cache|. - WebVector<WebFormControlElement> control_elements; - form.GetFormControlElements(control_elements); - control_elements[0].SetAttribute("name", "id"); - password_form = - CreatePasswordFormFromWebForm(form, nullptr, nullptr, &detector_cache); - EXPECT_TRUE(password_form); - ASSERT_EQ(1u, detector_cache.size()); - EXPECT_EQ(form.UniqueRendererFormId(), detector_cache.begin()->first); - EXPECT_TRUE(detector_cache.begin()->second.empty()); - histogram_tester.ExpectUniqueSample("PasswordManager.UsernameDetectionMethod", - UsernameDetectionMethod::BASE_HEURISTIC, - 2); - - // Clear the cache. The classifier will find username field and cache it. - detector_cache.clear(); - ASSERT_EQ(4u, control_elements.size()); - password_form = - CreatePasswordFormFromWebForm(form, nullptr, nullptr, &detector_cache); - EXPECT_TRUE(password_form); - ASSERT_EQ(1u, detector_cache.size()); - EXPECT_EQ(form.UniqueRendererFormId(), detector_cache.begin()->first); - ASSERT_EQ(1u, detector_cache.begin()->second.size()); - EXPECT_EQ(control_elements[0].UniqueRendererFormControlId(), - detector_cache.begin()->second[0]); - EXPECT_THAT( - histogram_tester.GetAllSamples("PasswordManager.UsernameDetectionMethod"), - testing::UnorderedElementsAre( - base::Bucket(UsernameDetectionMethod::BASE_HEURISTIC, 2), - base::Bucket(UsernameDetectionMethod::HTML_BASED_CLASSIFIER, 1))); - - // Change the attributes again ("username" is stronger signal than "login"), - // but keep the cache. The classifier's output should be the same. - control_elements[1].SetAttribute("name", "username"); - password_form = - CreatePasswordFormFromWebForm(form, nullptr, nullptr, &detector_cache); - EXPECT_TRUE(password_form); - ASSERT_EQ(1u, detector_cache.size()); - EXPECT_EQ(form.UniqueRendererFormId(), detector_cache.begin()->first); - ASSERT_EQ(1u, detector_cache.begin()->second.size()); - EXPECT_EQ(control_elements[0].UniqueRendererFormControlId(), - detector_cache.begin()->second[0]); - EXPECT_THAT( - histogram_tester.GetAllSamples("PasswordManager.UsernameDetectionMethod"), - testing::UnorderedElementsAre( - base::Bucket(UsernameDetectionMethod::BASE_HEURISTIC, 2), - base::Bucket(UsernameDetectionMethod::HTML_BASED_CLASSIFIER, 2))); -} - -TEST_F(PasswordFormConversionUtilsTest, HTMLDetectorCache_SkipSomePredictions) { - // The cache of HTML based username detector may contain several predictions - // (in the order of decreasing reliability) for the given form, but the - // detector should consider only |possible_usernames| passed to - // GetUsernameFieldBasedOnHtmlAttributes. For example, if a field has no user - // input while others has, the field cannot be an username field. - - PasswordFormBuilder builder(kTestFormActionURL); - builder.AddTextField("username", "12345", nullptr); - builder.AddTextField("email", "smith@google.com", nullptr); - builder.AddTextField("id", "12345", nullptr); - builder.AddPasswordField("password", "secret", nullptr); - builder.AddSubmitButton("submit"); - std::string html = builder.ProduceHTML(); - WebFormElement form; - LoadWebFormFromHTML(html, &form, nullptr); - WebVector<WebFormControlElement> control_elements; - form.GetFormControlElements(control_elements); - ASSERT_FALSE(control_elements.empty()); - - // Add predictions for "email" and "id" fields to the cache. - UsernameDetectorCache username_detector_cache; - username_detector_cache[control_elements[0].Form().UniqueRendererFormId()] = { - ToWebInputElement(&control_elements[1]) - ->UniqueRendererFormControlId(), // email - ToWebInputElement(&control_elements[2]) - ->UniqueRendererFormControlId()}; // id - - // A user typed only into "id" and "password" fields. So, the prediction for - // "email" field should be ignored despite it is more reliable than prediction - // for "id" field. - FieldDataManager field_data_manager; - field_data_manager.UpdateFieldDataMap( - control_elements[2], control_elements[2].Value().Utf16(), - FieldPropertiesFlags::USER_TYPED); // id - field_data_manager.UpdateFieldDataMap( - control_elements[3], control_elements[3].Value().Utf16(), - FieldPropertiesFlags::USER_TYPED); // password - - std::unique_ptr<PasswordForm> password_form = CreatePasswordFormFromWebForm( - form, &field_data_manager, nullptr, &username_detector_cache); - - ASSERT_TRUE(password_form); - EXPECT_EQ(base::UTF8ToUTF16("id"), password_form->username_element); -} - -TEST_F(PasswordFormConversionUtilsTest, - IdentifyingUsernameFieldsWithBaseHeuristic) { - // Each test case consists of a set of parameters to be plugged into the - // PasswordFormBuilder below, plus the corresponding expectations. - // The test data should not contain field names that are identified by the - // HTML based username detector, because with these tests only the base - // heuristic (i.e. select as username the field before the password field) - // is tested. - struct TestCase { - const char* autocomplete[3]; - const char* expected_username_element; - const char* expected_username_value; - const char* expected_all_possible_usernames; - } cases[] = { - // When no elements are marked with autocomplete='username', the text-type - // input field before the first password element should get selected as - // the username, and the rest should be marked as alternatives. - {{nullptr, nullptr, nullptr}, - "usrname2", - "William", - "John+usrname1, Smith+usrname3"}, - // When a sole element is marked with autocomplete='username', it should - // be treated as the username, but other text fields should be added to - // |all_possible_usernames|. - {{"username", nullptr, nullptr}, - "usrname1", - "John", - "William+usrname2, Smith+usrname3"}, - {{nullptr, "username", nullptr}, - "usrname2", - "William", - "John+usrname1, Smith+usrname3"}, - {{nullptr, nullptr, "username"}, - "usrname3", - "Smith", - "John+usrname1, William+usrname2"}, - // When >=2 elements have the attribute, the first should be selected as - // the username, and the rest should go to |all_possible_usernames|. - {{"username", "username", nullptr}, - "usrname1", - "John", - "William+usrname2, Smith+usrname3"}, - {{nullptr, "username", "username"}, - "usrname2", - "William", - "John+usrname1, Smith+usrname3"}, - {{"username", nullptr, "username"}, - "usrname1", - "John", - "William+usrname2, Smith+usrname3"}, - {{"username", "username", "username"}, - "usrname1", - "John", - "William+usrname2, Smith+usrname3"}, - // When there is an empty autocomplete attribute (i.e. autocomplete=""), - // it should have the same effect as having no attribute whatsoever. - {{"", "", ""}, "usrname2", "William", "John+usrname1, Smith+usrname3"}, - {{"", "", "username"}, - "usrname3", - "Smith", - "John+usrname1, William+usrname2"}, - {{"username", "", "username"}, - "usrname1", - "John", - "William+usrname2, Smith+usrname3"}, - // It should not matter if attribute values are upper or mixed case. - {{"USERNAME", nullptr, "uSeRNaMe"}, - "usrname1", - "John", - "William+usrname2, Smith+usrname3"}, - {{"uSeRNaMe", nullptr, "USERNAME"}, - "usrname1", - "John", - "William+usrname2, Smith+usrname3"}}; - - for (size_t i = 0; i < base::size(cases); ++i) { - for (size_t nonempty_username_fields = 0; nonempty_username_fields < 2; - ++nonempty_username_fields) { - SCOPED_TRACE(testing::Message() - << "Iteration " << i << " " - << (nonempty_username_fields ? "nonempty" : "empty")); - - // Repeat each test once with empty, and once with non-empty usernames. - // In the former case, no empty all_possible_usernames should be saved. - const char* names[3]; - if (nonempty_username_fields) { - names[0] = "John"; - names[1] = "William"; - names[2] = "Smith"; - } else { - names[0] = names[1] = names[2] = ""; - } - - PasswordFormBuilder builder(kTestFormActionURL); - builder.AddTextField("usrname1", names[0], cases[i].autocomplete[0]); - builder.AddTextField("usrname2", names[1], cases[i].autocomplete[1]); - builder.AddPasswordField("password", "secret", nullptr); - builder.AddTextField("usrname3", names[2], cases[i].autocomplete[2]); - builder.AddPasswordField("password2", "othersecret", nullptr); - builder.AddSubmitButton("submit"); - std::string html = builder.ProduceHTML(); - - std::unique_ptr<PasswordForm> password_form = - LoadHTMLAndConvertForm(html, nullptr, false); - ASSERT_TRUE(password_form); - - EXPECT_FALSE(password_form->only_for_fallback); - EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_username_element), - password_form->username_element); - - if (nonempty_username_fields) { - EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_username_value), - password_form->username_value); - EXPECT_EQ( - base::UTF8ToUTF16(cases[i].expected_all_possible_usernames), - ValueElementVectorToString(password_form->all_possible_usernames)); - } else { - EXPECT_TRUE(password_form->username_value.empty()); - EXPECT_TRUE(password_form->all_possible_usernames.empty()); - } - - // Do a basic sanity check that we are still having a password field. - EXPECT_EQ(base::UTF8ToUTF16("password"), password_form->password_element); - EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->password_value); - } - } -} - -TEST_F(PasswordFormConversionUtilsTest, IdentifyingTwoPasswordFields) { - // Each test case consists of a set of parameters to be plugged into the - // PasswordFormBuilder below, plus the corresponding expectations. - struct TestCase { - const char* password_values[2]; - const char* expected_password_element; - const char* expected_password_value; - const char* expected_new_password_element; - const char* expected_new_password_value; - const char* expected_confirmation_element; - } cases[] = { - // Two non-empty fields with the same value should be treated as a new - // password field plus a confirmation field for the new password. - {{"alpha", "alpha"}, "", "", "password1", "alpha", "password2"}, - // The same goes if the fields are yet empty: we speculate that we will - // identify them as new password fields once they are filled out, and we - // want to keep our abstract interpretation of the form less flaky. - {{"", ""}, "password1", "", "password2", "", ""}, - // Two different values should be treated as a password change form, one - // that also asks for the current password, but only once for the new. - {{"alpha", ""}, "password1", "alpha", "password2", "", ""}, - {{"", "beta"}, "password1", "", "password2", "beta", ""}, - {{"alpha", "beta"}, "password1", "alpha", "password2", "beta", ""}}; - - for (size_t i = 0; i < base::size(cases); ++i) { - SCOPED_TRACE(testing::Message() << "Iteration " << i); - - PasswordFormBuilder builder(kTestFormActionURL); - builder.AddTextField("usrname1", "William", nullptr); - builder.AddPasswordField("password1", cases[i].password_values[0], nullptr); - builder.AddTextField("usrname2", "Smith", nullptr); - builder.AddPasswordField("password2", cases[i].password_values[1], nullptr); - builder.AddSubmitButton("submit"); - std::string html = builder.ProduceHTML(); - - std::unique_ptr<PasswordForm> password_form = - LoadHTMLAndConvertForm(html, nullptr, false); - ASSERT_TRUE(password_form); - - EXPECT_FALSE(password_form->only_for_fallback); - EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_element), - password_form->password_element); - EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_value), - password_form->password_value); - EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_element), - password_form->new_password_element); - EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_value), - password_form->new_password_value); - EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_confirmation_element), - password_form->confirmation_password_element); - - // Do a basic sanity check that we are still selecting the right username. - EXPECT_EQ(base::UTF8ToUTF16("usrname1"), password_form->username_element); - EXPECT_EQ(base::UTF8ToUTF16("William"), password_form->username_value); - EXPECT_THAT( - password_form->all_possible_usernames, - testing::ElementsAre(ValueElementPair(base::UTF8ToUTF16("Smith"), - base::UTF8ToUTF16("usrname2")))); - } -} - -TEST_F(PasswordFormConversionUtilsTest, IdentifyingThreePasswordFields) { - // Each test case consists of a set of parameters to be plugged into the - // PasswordFormBuilder below, plus the corresponding expectations. - struct TestCase { - const char* password_values[3]; - const char* expected_password_element; - const char* expected_password_value; - const char* expected_new_password_element; - const char* expected_new_password_value; - const char* expected_confirmation_element; - } cases[] = { - // Two fields with the same value, and one different: we should treat this - // as a password change form with confirmation for the new password. Note - // that we only recognize (current + new + new) and (new + new + current) - // without autocomplete attributes. - {{"alpha", "", ""}, "password1", "alpha", "password2", "", "password3"}, - {{"", "beta", "beta"}, "password1", "", "password2", "beta", "password3"}, - {{"alpha", "beta", "beta"}, - "password1", - "alpha", - "password2", - "beta", - "password3"}, - // If confirmed password comes first, assume that the third password - // field is related to security question, SSN, or credit card and ignore - // it. - {{"beta", "beta", "alpha"}, "", "", "password1", "beta", "password2"}, - // If the fields are yet empty, we speculate that we will identify them as - // (current + new + new) once they are filled out, so we should classify - // them the same for now to keep our abstract interpretation less flaky. - {{"", "", ""}, "password1", "", "password2", "", "password3"}}; - - for (size_t i = 0; i < base::size(cases); ++i) { - SCOPED_TRACE(testing::Message() << "Iteration " << i); - - PasswordFormBuilder builder(kTestFormActionURL); - builder.AddTextField("usrname1", "William", nullptr); - builder.AddPasswordField("password1", cases[i].password_values[0], nullptr); - builder.AddPasswordField("password2", cases[i].password_values[1], nullptr); - builder.AddTextField("usrname2", "Smith", nullptr); - builder.AddPasswordField("password3", cases[i].password_values[2], nullptr); - builder.AddSubmitButton("submit"); - std::string html = builder.ProduceHTML(); - - std::unique_ptr<PasswordForm> password_form = - LoadHTMLAndConvertForm(html, nullptr, false); - ASSERT_TRUE(password_form); - - EXPECT_FALSE(password_form->only_for_fallback); - EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_element), - password_form->password_element); - EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_value), - password_form->password_value); - EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_element), - password_form->new_password_element); - EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_value), - password_form->new_password_value); - EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_confirmation_element), - password_form->confirmation_password_element); - - // Do a basic sanity check that we are still selecting the right username. - EXPECT_EQ(base::UTF8ToUTF16("usrname1"), password_form->username_element); - EXPECT_EQ(base::UTF8ToUTF16("William"), password_form->username_value); - EXPECT_THAT( - password_form->all_possible_usernames, - testing::ElementsAre(ValueElementPair(base::UTF8ToUTF16("Smith"), - base::UTF8ToUTF16("usrname2")))); - } -} - -TEST_F(PasswordFormConversionUtilsTest, - IdentifyingPasswordFieldsWithAutocompleteAttributes) { - // Each test case consists of a set of parameters to be plugged into the - // PasswordFormBuilder below, plus the corresponding expectations. - // The test data should not contain field names that are identified by the - // HTML based username detector, because with these tests only the base - // heuristic (i.e. select as username the field before the password field) - // is tested. - struct TestCase { - const char* autocomplete[3]; - const char* expected_password_element; - const char* expected_password_value; - const char* expected_new_password_element; - const char* expected_new_password_value; - bool expected_new_password_marked_by_site; - const char* expected_username_element; - const char* expected_username_value; - } cases[] = { - // When there are elements marked with autocomplete='current-password', - // but no elements with 'new-password', we should treat the first of the - // former kind as the current password, and ignore all other password - // fields, assuming they are not intentionally not marked. They might be - // for other purposes, such as PINs, OTPs, and the like. Actual values in - // the password fields should be ignored in all cases below. - // Username is the element just before the first 'current-password' (even - // if 'new-password' comes earlier). If no 'current-password', then the - // element just before the first 'new-passwords'. - {{"current-password", nullptr, nullptr}, - "password1", - "alpha", - "", - "", - false, - "usrname1", - "William"}, - {{nullptr, "current-password", nullptr}, - "password2", - "beta", - "", - "", - false, - "usrname2", - "Smith"}, - {{nullptr, nullptr, "current-password"}, - "password3", - "gamma", - "", - "", - false, - "usrname2", - "Smith"}, - {{nullptr, "current-password", "current-password"}, - "password2", - "beta", - "", - "", - false, - "usrname2", - "Smith"}, - {{"current-password", nullptr, "current-password"}, - "password1", - "alpha", - "", - "", - false, - "usrname1", - "William"}, - {{"current-password", "current-password", nullptr}, - "password1", - "alpha", - "", - "", - false, - "usrname1", - "William"}, - {{"current-password", "current-password", "current-password"}, - "password1", - "alpha", - "", - "", - false, - "usrname1", - "William"}, - // The same goes vice versa for autocomplete='new-password'. - {{"new-password", nullptr, nullptr}, - "", - "", - "password1", - "alpha", - true, - "usrname1", - "William"}, - {{nullptr, "new-password", nullptr}, - "", - "", - "password2", - "beta", - true, - "usrname2", - "Smith"}, - {{nullptr, nullptr, "new-password"}, - "", - "", - "password3", - "gamma", - true, - "usrname2", - "Smith"}, - {{nullptr, "new-password", "new-password"}, - "", - "", - "password2", - "beta", - true, - "usrname2", - "Smith"}, - {{"new-password", nullptr, "new-password"}, - "", - "", - "password1", - "alpha", - true, - "usrname1", - "William"}, - {{"new-password", "new-password", nullptr}, - "", - "", - "password1", - "alpha", - true, - "usrname1", - "William"}, - {{"new-password", "new-password", "new-password"}, - "", - "", - "password1", - "alpha", - true, - "usrname1", - "William"}, - // When there is one element marked with autocomplete='current-password', - // and one with 'new-password', just comply. Ignore the unmarked password - // field(s) for the same reason as above. - {{"current-password", "new-password", nullptr}, - "password1", - "alpha", - "password2", - "beta", - true, - "usrname1", - "William"}, - {{"current-password", nullptr, "new-password"}, - "password1", - "alpha", - "password3", - "gamma", - true, - "usrname1", - "William"}, - {{nullptr, "current-password", "new-password"}, - "password2", - "beta", - "password3", - "gamma", - true, - "usrname2", - "Smith"}, - {{"new-password", "current-password", nullptr}, - "password2", - "beta", - "password1", - "alpha", - true, - "usrname2", - "Smith"}, - {{"new-password", nullptr, "current-password"}, - "password3", - "gamma", - "password1", - "alpha", - true, - "usrname2", - "Smith"}, - {{nullptr, "new-password", "current-password"}, - "password3", - "gamma", - "password2", - "beta", - true, - "usrname2", - "Smith"}, - // In case of duplicated elements of either kind, go with the first one of - // its kind. - {{"current-password", "current-password", "new-password"}, - "password1", - "alpha", - "password3", - "gamma", - true, - "usrname1", - "William"}, - {{"current-password", "new-password", "current-password"}, - "password1", - "alpha", - "password2", - "beta", - true, - "usrname1", - "William"}, - {{"new-password", "current-password", "current-password"}, - "password2", - "beta", - "password1", - "alpha", - true, - "usrname2", - "Smith"}, - {{"current-password", "new-password", "new-password"}, - "password1", - "alpha", - "password2", - "beta", - true, - "usrname1", - "William"}, - {{"new-password", "current-password", "new-password"}, - "password2", - "beta", - "password1", - "alpha", - true, - "usrname2", - "Smith"}, - {{"new-password", "new-password", "current-password"}, - "password3", - "gamma", - "password1", - "alpha", - true, - "usrname2", - "Smith"}, - // When there is an empty autocomplete attribute (i.e. autocomplete=""), - // it should have the same effect as having no attribute whatsoever. - {{"current-password", "", ""}, - "password1", - "alpha", - "", - "", - false, - "usrname1", - "William"}, - {{"", "", "new-password"}, - "", - "", - "password3", - "gamma", - true, - "usrname2", - "Smith"}, - {{"", "new-password", ""}, - "", - "", - "password2", - "beta", - true, - "usrname2", - "Smith"}, - {{"", "current-password", "current-password"}, - "password2", - "beta", - "", - "", - false, - "usrname2", - "Smith"}, - {{"new-password", "", "new-password"}, - "", - "", - "password1", - "alpha", - true, - "usrname1", - "William"}, - {{"new-password", "", "current-password"}, - "password3", - "gamma", - "password1", - "alpha", - true, - "usrname2", - "Smith"}, - // It should not matter if attribute values are upper or mixed case. - {{nullptr, "current-password", nullptr}, - "password2", - "beta", - "", - "", - false, - "usrname2", - "Smith"}, - {{nullptr, "CURRENT-PASSWORD", nullptr}, - "password2", - "beta", - "", - "", - false, - "usrname2", - "Smith"}, - {{nullptr, "new-password", nullptr}, - "", - "", - "password2", - "beta", - true, - "usrname2", - "Smith"}, - {{nullptr, "nEw-PaSsWoRd", nullptr}, - "", - "", - "password2", - "beta", - true, - "usrname2", - "Smith"}}; - - for (size_t i = 0; i < base::size(cases); ++i) { - SCOPED_TRACE(testing::Message() << "Iteration " << i); - - PasswordFormBuilder builder(kTestFormActionURL); - builder.AddPasswordField("pin1", "123456", nullptr); - builder.AddPasswordField("pin2", "789101", nullptr); - builder.AddTextField("usrname1", "William", nullptr); - builder.AddPasswordField("password1", "alpha", cases[i].autocomplete[0]); - builder.AddTextField("usrname2", "Smith", nullptr); - builder.AddPasswordField("password2", "beta", cases[i].autocomplete[1]); - builder.AddPasswordField("password3", "gamma", cases[i].autocomplete[2]); - builder.AddSubmitButton("submit"); - std::string html = builder.ProduceHTML(); - - std::unique_ptr<PasswordForm> password_form = - LoadHTMLAndConvertForm(html, nullptr, false); - ASSERT_TRUE(password_form); - - EXPECT_FALSE(password_form->only_for_fallback); - // In the absence of username autocomplete attributes, the username should - // be the text input field just before 'current-password' or before - // 'new-password', if there is no 'current-password'. - EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_username_element), - password_form->username_element); - EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_username_value), - password_form->username_value); - if (strcmp(cases[i].expected_username_value, "William") == 0) { - EXPECT_THAT( - password_form->all_possible_usernames, - testing::ElementsAre(ValueElementPair( - base::UTF8ToUTF16("Smith"), base::UTF8ToUTF16("usrname2")))); - } else { - EXPECT_THAT( - password_form->all_possible_usernames, - testing::ElementsAre(ValueElementPair( - base::UTF8ToUTF16("William"), base::UTF8ToUTF16("usrname1")))); - } - EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_element), - password_form->password_element); - EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_value), - password_form->password_value); - EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_element), - password_form->new_password_element); - EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_value), - password_form->new_password_value); - EXPECT_EQ(cases[i].expected_new_password_marked_by_site, - password_form->new_password_marked_by_site); - } -} - -TEST_F(PasswordFormConversionUtilsTest, - UsernameDetection_AutocompleteAttribute) { - PasswordFormBuilder builder(kTestFormActionURL); - builder.AddTextField("username", "JohnSmith", "username"); - builder.AddTextField("Full name", "John A. Smith", nullptr); - builder.AddPasswordField("password", "secret", nullptr); - builder.AddSubmitButton("submit"); - std::string html = builder.ProduceHTML(); - - base::HistogramTester histogram_tester; - std::unique_ptr<PasswordForm> password_form = - LoadHTMLAndConvertForm(html, nullptr, false); - ASSERT_TRUE(password_form); - EXPECT_EQ(base::UTF8ToUTF16("username"), password_form->username_element); - EXPECT_EQ(base::UTF8ToUTF16("JohnSmith"), password_form->username_value); - histogram_tester.ExpectUniqueSample( - "PasswordManager.UsernameDetectionMethod", - UsernameDetectionMethod::AUTOCOMPLETE_ATTRIBUTE, 1); -} - -TEST_F(PasswordFormConversionUtilsTest, IgnoreInvisibledTextFields) { - PasswordFormBuilder builder(kTestFormActionURL); - - builder.AddNonDisplayedTextField("nondisplayed1", "nodispalyed_value1"); - builder.AddNonVisibleTextField("nonvisible1", "nonvisible_value1"); - builder.AddTextField("username", "johnsmith", nullptr); - builder.AddNonDisplayedTextField("nondisplayed2", "nodispalyed_value2"); - builder.AddNonVisiblePasswordField("nonvisible2", "nonvisible_value2"); - builder.AddPasswordField("password", "secret", nullptr); - builder.AddPasswordField("password", "secret", nullptr); - builder.AddSubmitButton("submit"); - std::string html = builder.ProduceHTML(); - - std::unique_ptr<PasswordForm> password_form = - LoadHTMLAndConvertForm(html, nullptr, false); - ASSERT_TRUE(password_form); - EXPECT_FALSE(password_form->only_for_fallback); - EXPECT_EQ(base::UTF8ToUTF16("username"), password_form->username_element); - EXPECT_EQ(base::UTF8ToUTF16("johnsmith"), password_form->username_value); - EXPECT_EQ(base::UTF8ToUTF16(""), password_form->password_element); - EXPECT_EQ(base::UTF8ToUTF16("password"), password_form->new_password_element); - EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->new_password_value); -} - -TEST_F(PasswordFormConversionUtilsTest, IgnoreInvisiblLoginPairs) { - PasswordFormBuilder builder(kTestFormActionURL); - - builder.AddNonDisplayedTextField("nondisplayed1", "nodispalyed_value1"); - builder.AddNonDisplayedPasswordField("nondisplayed2", "nodispalyed_value2"); - builder.AddNonVisibleTextField("nonvisible1", "nonvisible_value1"); - builder.AddNonVisiblePasswordField("nonvisible2", "nonvisible_value2"); - builder.AddTextField("username", "johnsmith", nullptr); - builder.AddNonVisibleTextField("nonvisible3", "nonvisible_value3"); - builder.AddNonVisiblePasswordField("nonvisible4", "nonvisible_value4"); - builder.AddNonDisplayedTextField("nondisplayed3", "nodispalyed_value3"); - builder.AddNonDisplayedPasswordField("nondisplayed4", "nodispalyed_value4"); - builder.AddPasswordField("password", "secret", nullptr); - builder.AddPasswordField("password", "secret", nullptr); - builder.AddSubmitButton("submit"); - std::string html = builder.ProduceHTML(); - - std::unique_ptr<PasswordForm> password_form = - LoadHTMLAndConvertForm(html, nullptr, false); - ASSERT_TRUE(password_form); - EXPECT_FALSE(password_form->only_for_fallback); - EXPECT_EQ(base::UTF8ToUTF16("username"), password_form->username_element); - EXPECT_EQ(base::UTF8ToUTF16("johnsmith"), password_form->username_value); - EXPECT_EQ(base::UTF8ToUTF16(""), password_form->password_element); - EXPECT_EQ(base::UTF8ToUTF16("password"), password_form->new_password_element); - EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->new_password_value); -} - -TEST_F(PasswordFormConversionUtilsTest, OnlyNonDisplayedLoginPair) { - PasswordFormBuilder builder(kTestFormActionURL); - - builder.AddNonDisplayedTextField("username", "William"); - builder.AddNonDisplayedPasswordField("password", "secret"); - builder.AddSubmitButton("submit"); - std::string html = builder.ProduceHTML(); - - std::unique_ptr<PasswordForm> password_form = - LoadHTMLAndConvertForm(html, nullptr, false); - ASSERT_TRUE(password_form); - EXPECT_FALSE(password_form->only_for_fallback); - EXPECT_EQ(base::UTF8ToUTF16("username"), - password_form->username_element); - EXPECT_EQ(base::UTF8ToUTF16("William"), - password_form->username_value); - EXPECT_EQ(base::UTF8ToUTF16("password"), - password_form->password_element); - EXPECT_EQ(base::UTF8ToUTF16("secret"), - password_form->password_value); -} - -TEST_F(PasswordFormConversionUtilsTest, OnlyNonVisibleLoginPair) { - PasswordFormBuilder builder(kTestFormActionURL); - - builder.AddNonVisibleTextField("username", "William"); - builder.AddNonVisiblePasswordField("password", "secret"); - builder.AddSubmitButton("submit"); - std::string html = builder.ProduceHTML(); - - std::unique_ptr<PasswordForm> password_form = - LoadHTMLAndConvertForm(html, nullptr, false); - ASSERT_TRUE(password_form); - EXPECT_FALSE(password_form->only_for_fallback); - EXPECT_EQ(base::UTF8ToUTF16("username"), password_form->username_element); - EXPECT_EQ(base::UTF8ToUTF16("William"), password_form->username_value); - EXPECT_EQ(base::UTF8ToUTF16("password"), password_form->password_element); - EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->password_value); -} - -TEST_F(PasswordFormConversionUtilsTest, VisiblePasswordAndInvisibleUsername) { - PasswordFormBuilder builder(kTestFormActionURL); - - builder.AddNonDisplayedTextField("username", "William"); - builder.AddPasswordField("password", "secret", nullptr); - builder.AddSubmitButton("submit"); - std::string html = builder.ProduceHTML(); - - std::unique_ptr<PasswordForm> password_form = - LoadHTMLAndConvertForm(html, nullptr, false); - ASSERT_TRUE(password_form); - EXPECT_FALSE(password_form->only_for_fallback); - EXPECT_EQ(base::UTF8ToUTF16("username"), password_form->username_element); - EXPECT_EQ(base::UTF8ToUTF16("William"), password_form->username_value); - EXPECT_EQ(base::UTF8ToUTF16("password"), password_form->password_element); - EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->password_value); -} - -TEST_F(PasswordFormConversionUtilsTest, - InvisiblePassword_LatestUsernameIsVisible) { - PasswordFormBuilder builder(kTestFormActionURL); - - builder.AddNonDisplayedTextField("search", "query"); - builder.AddTextField("username", "William", nullptr); - builder.AddNonDisplayedPasswordField("password", "secret"); - builder.AddSubmitButton("submit"); - std::string html = builder.ProduceHTML(); - - std::unique_ptr<PasswordForm> password_form = - LoadHTMLAndConvertForm(html, nullptr, false); - ASSERT_TRUE(password_form); - EXPECT_FALSE(password_form->only_for_fallback); - EXPECT_EQ(base::UTF8ToUTF16("username"), password_form->username_element); - EXPECT_EQ(base::UTF8ToUTF16("William"), password_form->username_value); - EXPECT_EQ(base::UTF8ToUTF16("password"), password_form->password_element); - EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->password_value); -} - -TEST_F(PasswordFormConversionUtilsTest, - InvisiblePassword_LatestUsernameIsInvisible) { - PasswordFormBuilder builder(kTestFormActionURL); - - builder.AddTextField("search", "query", nullptr); - builder.AddNonDisplayedTextField("username", "William"); - builder.AddNonDisplayedPasswordField("password", "secret"); - builder.AddSubmitButton("submit"); - std::string html = builder.ProduceHTML(); - - std::unique_ptr<PasswordForm> password_form = - LoadHTMLAndConvertForm(html, nullptr, false); - ASSERT_TRUE(password_form); - EXPECT_FALSE(password_form->only_for_fallback); - EXPECT_EQ(base::UTF8ToUTF16("username"), password_form->username_element); - EXPECT_EQ(base::UTF8ToUTF16("William"), password_form->username_value); - EXPECT_EQ(base::UTF8ToUTF16("password"), password_form->password_element); - EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->password_value); -} - -// Checks that username/password fields are detected based on user input even if -// visibility heuristics disagree. -TEST_F(PasswordFormConversionUtilsTest, UserInput) { - PasswordFormBuilder builder(kTestFormActionURL); - - builder.AddNonVisibleTextField("nonvisible_text", "actual_username"); - builder.AddTextField("visible_text", "fake_username", nullptr); - - builder.AddNonVisiblePasswordField("nonvisible_password", "actual_password"); - builder.AddPasswordField("visible_password", "fake_password", nullptr); - builder.AddSubmitButton("submit"); - std::string html = builder.ProduceHTML(); - - WebFormElement form; - LoadWebFormFromHTML(html, &form, nullptr); - - FieldDataManager field_data_manager; - WebVector<WebFormControlElement> control_elements; - form.GetFormControlElements(control_elements); - ASSERT_EQ("nonvisible_text", control_elements[0].NameForAutofill().Utf8()); - field_data_manager.UpdateFieldDataMap(control_elements[0], - control_elements[0].Value().Utf16(), - FieldPropertiesFlags::USER_TYPED); - ASSERT_EQ("nonvisible_password", - control_elements[2].NameForAutofill().Utf8()); - field_data_manager.UpdateFieldDataMap(control_elements[2], - control_elements[2].Value().Utf16(), - FieldPropertiesFlags::USER_TYPED); - - std::unique_ptr<PasswordForm> password_form = CreatePasswordFormFromWebForm( - form, &field_data_manager, nullptr, nullptr); - - ASSERT_TRUE(password_form); - EXPECT_FALSE(password_form->only_for_fallback); - EXPECT_EQ(base::UTF8ToUTF16("nonvisible_text"), - password_form->username_element); - EXPECT_EQ(base::UTF8ToUTF16("actual_username"), - password_form->username_value); - EXPECT_EQ(base::UTF8ToUTF16("nonvisible_password"), - password_form->password_element); - EXPECT_EQ(base::UTF8ToUTF16("actual_password"), - password_form->password_value); - EXPECT_EQ(base::UTF8ToUTF16(""), password_form->new_password_element); - EXPECT_EQ(base::UTF8ToUTF16(""), password_form->new_password_value); -} - -TEST_F(PasswordFormConversionUtilsTest, TypedPasswordAndUsernameCachedOnPage) { - PasswordFormBuilder builder(kTestFormActionURL); - // The heuristics should consider only password field with user input (i.e. - // password_with_user_input?) and visible username fields (i.e. nickname, - // visible_text, captcha) since there is no username field with user input. - builder.AddNonDisplayedPasswordField("nondisplayed1", "fake_password"); - builder.AddNonDisplayedTextField("nondisplayed1", "fake_username1"); - builder.AddTextField("nickname", "bob", nullptr); - builder.AddTextField("visible_text", "cached_username", - nullptr); // Username. - builder.AddNonVisibleTextField("nonvisible_text", "fake_username2"); - builder.AddNonVisiblePasswordField("nonvisible_password", "not_password"); - builder.AddNonVisibleTextField("nonvisible_text", "fake_username2"); - builder.AddPasswordField("password_wo_user_input", "", nullptr); - builder.AddNonVisibleTextField("nonvisible_text", ""); - builder.AddPasswordField("password_with_user_input1", "actual_password", - nullptr); // Password to save. - builder.AddPasswordField("password_with_user_input2", "actual_password", - nullptr); - builder.AddTextField("captcha", "12345", nullptr); - builder.AddSubmitButton("submit"); - std::string html = builder.ProduceHTML(); - - WebFormElement form; - LoadWebFormFromHTML(html, &form, nullptr); - - FieldDataManager field_data_manager; - WebVector<WebFormControlElement> control_elements; - form.GetFormControlElements(control_elements); - ASSERT_EQ("password_with_user_input1", - control_elements[9].NameForAutofill().Utf8()); - field_data_manager.UpdateFieldDataMap(control_elements[9], - control_elements[9].Value().Utf16(), - FieldPropertiesFlags::USER_TYPED); - ASSERT_EQ("password_with_user_input2", - control_elements[10].NameForAutofill().Utf8()); - field_data_manager.UpdateFieldDataMap(control_elements[10], - control_elements[10].Value().Utf16(), - FieldPropertiesFlags::USER_TYPED); - - std::unique_ptr<PasswordForm> password_form = CreatePasswordFormFromWebForm( - form, &field_data_manager, nullptr, nullptr); - - ASSERT_TRUE(password_form); - EXPECT_FALSE(password_form->only_for_fallback); - - EXPECT_EQ(base::UTF8ToUTF16("visible_text"), password_form->username_element); - EXPECT_EQ(base::UTF8ToUTF16("cached_username"), - password_form->username_value); - - EXPECT_EQ(base::string16(), password_form->password_element); - EXPECT_EQ(base::UTF8ToUTF16(""), password_form->password_value); - - EXPECT_EQ(base::UTF8ToUTF16("password_with_user_input1"), - password_form->new_password_element); - EXPECT_EQ(base::UTF8ToUTF16("actual_password"), - password_form->new_password_value); -} - -TEST_F(PasswordFormConversionUtilsTest, TypedPasswordAndInvisibleUsername) { - PasswordFormBuilder builder(kTestFormActionURL); - // The heuristics should consider only password field with user input (i.e. - // password_with_user_input?) and invisible username fields since all username - // fields have no user input and are invisible. - builder.AddNonDisplayedPasswordField("nondisplayed1", "fake_password"); - builder.AddNonDisplayedTextField("nondisplayed1", "fake_username1"); - builder.AddNonVisibleTextField("nickname", "bob"); - builder.AddNonVisibleTextField("this_is_username", "invisible_username"); - builder.AddNonVisiblePasswordField("nonvisible_password", "not_password"); - builder.AddPasswordField("password_wo_user_input", "", nullptr); - builder.AddNonVisiblePasswordField("nonvisible_password2", ""); - builder.AddPasswordField("password_with_user_input1", "actual_password", - nullptr); // Password to save. - builder.AddNonVisibleTextField("nonvisible_text3", "---H09-$%"); - builder.AddPasswordField("password_with_user_input2", "actual_password", - nullptr); - builder.AddNonVisibleTextField("nonvisible_text3", "debug_info"); - builder.AddSubmitButton("submit"); - std::string html = builder.ProduceHTML(); - - WebFormElement form; - LoadWebFormFromHTML(html, &form, nullptr); - - FieldDataManager field_data_manager; - WebVector<WebFormControlElement> control_elements; - form.GetFormControlElements(control_elements); - ASSERT_EQ("password_with_user_input1", - control_elements[7].NameForAutofill().Utf8()); - field_data_manager.UpdateFieldDataMap(control_elements[7], - control_elements[7].Value().Utf16(), - FieldPropertiesFlags::USER_TYPED); - ASSERT_EQ("password_with_user_input2", - control_elements[9].NameForAutofill().Utf8()); - field_data_manager.UpdateFieldDataMap(control_elements[9], - control_elements[9].Value().Utf16(), - FieldPropertiesFlags::USER_TYPED); - - std::unique_ptr<PasswordForm> password_form = CreatePasswordFormFromWebForm( - form, &field_data_manager, nullptr, nullptr); - - ASSERT_TRUE(password_form); - EXPECT_FALSE(password_form->only_for_fallback); - - EXPECT_EQ(base::UTF8ToUTF16("this_is_username"), - password_form->username_element); - EXPECT_EQ(base::UTF8ToUTF16("invisible_username"), - password_form->username_value); - - EXPECT_EQ(base::string16(), password_form->password_element); - EXPECT_EQ(base::UTF8ToUTF16(""), password_form->password_value); - - EXPECT_EQ(base::UTF8ToUTF16("password_with_user_input1"), - password_form->new_password_element); - EXPECT_EQ(base::UTF8ToUTF16("actual_password"), - password_form->new_password_value); -} - -TEST_F(PasswordFormConversionUtilsTest, InvalidFormDueToBadActionURL) { - PasswordFormBuilder builder("invalid_target"); - builder.AddTextField("username", "JohnSmith", nullptr); - builder.AddSubmitButton("submit"); - builder.AddPasswordField("password", "secret", nullptr); - std::string html = builder.ProduceHTML(); - - std::unique_ptr<PasswordForm> password_form = - LoadHTMLAndConvertForm(html, nullptr, false); - EXPECT_FALSE(password_form); -} - -TEST_F(PasswordFormConversionUtilsTest, InvalidFormDueToNoPasswordFields) { - PasswordFormBuilder builder(kTestFormActionURL); - builder.AddTextField("username1", "John", nullptr); - builder.AddTextField("username2", "Smith", nullptr); - builder.AddSubmitButton("submit"); - std::string html = builder.ProduceHTML(); - - std::unique_ptr<PasswordForm> password_form = - LoadHTMLAndConvertForm(html, nullptr, false); - EXPECT_FALSE(password_form); -} - -TEST_F(PasswordFormConversionUtilsTest, ConfusingPasswordFields) { - // Each test case consists of a set of parameters to be plugged into the - // PasswordFormBuilder below. - const char* cases[][3] = { - // No autocomplete attributes to guide us, and we see: - // * three password values that are all different, - // * three password values that are all the same; - // * three password values with the first and last matching. - // In any case, we should just give up on this form. - {"alpha", "beta", "gamma"}, - {"alpha", "alpha", "alpha"}, - {"alpha", "beta", "alpha"}}; - - for (size_t i = 0; i < base::size(cases); ++i) { - SCOPED_TRACE(testing::Message() << "Iteration " << i); - - PasswordFormBuilder builder(kTestFormActionURL); - builder.AddTextField("username1", "John", nullptr); - builder.AddPasswordField("password1", cases[i][0], nullptr); - builder.AddPasswordField("password2", cases[i][1], nullptr); - builder.AddPasswordField("password3", cases[i][2], nullptr); - builder.AddSubmitButton("submit"); - std::string html = builder.ProduceHTML(); - - std::unique_ptr<PasswordForm> password_form = - LoadHTMLAndConvertForm(html, nullptr, false); - ASSERT_TRUE(password_form); - EXPECT_FALSE(password_form->only_for_fallback); - EXPECT_EQ(base::UTF8ToUTF16("username1"), password_form->username_element); - EXPECT_EQ(base::UTF8ToUTF16("John"), password_form->username_value); - EXPECT_EQ(base::UTF8ToUTF16("password1"), password_form->password_element); - EXPECT_EQ(base::UTF8ToUTF16(cases[i][0]), password_form->password_value); - } -} - -TEST_F(PasswordFormConversionUtilsTest, - ManyPasswordFieldsWithoutAutocompleteAttributes) { - PasswordFormBuilder builder(kTestFormActionURL); - builder.AddTextField("username1", "John", nullptr); - builder.AddPasswordField("password1", "alpha", nullptr); - builder.AddPasswordField("password2", "alpha", nullptr); - builder.AddPasswordField("password3", "alpha", nullptr); - builder.AddPasswordField("password4", "alpha", nullptr); - builder.AddSubmitButton("submit"); - std::string html = builder.ProduceHTML(); - - std::unique_ptr<PasswordForm> password_form = - LoadHTMLAndConvertForm(html, nullptr, false); - ASSERT_TRUE(password_form); - EXPECT_FALSE(password_form->only_for_fallback); - EXPECT_EQ(base::UTF8ToUTF16("username1"), password_form->username_element); - EXPECT_EQ(base::UTF8ToUTF16("John"), password_form->username_value); - EXPECT_EQ(base::UTF8ToUTF16("password1"), password_form->password_element); - EXPECT_EQ(base::UTF8ToUTF16("alpha"), password_form->password_value); -} - -TEST_F(PasswordFormConversionUtilsTest, SetOtherPossiblePasswords) { - PasswordFormBuilder builder(kTestFormActionURL); - builder.AddTextField("username1", "John", nullptr); - builder.AddPasswordField("password1", "alpha1", nullptr); - builder.AddPasswordField("password2", "alpha2", nullptr); - builder.AddPasswordField("password3", "alpha3", nullptr); - builder.AddPasswordField("password4", "alpha4", nullptr); - builder.AddSubmitButton("submit"); - std::string html = builder.ProduceHTML(); - - std::unique_ptr<PasswordForm> password_form = - LoadHTMLAndConvertForm(html, nullptr, false); - ASSERT_TRUE(password_form); - EXPECT_FALSE(password_form->form_has_autofilled_value); - - // Make sure we have all possible passwords along with the username info. - EXPECT_EQ(base::ASCIIToUTF16("username1"), password_form->username_element); - EXPECT_EQ(base::ASCIIToUTF16("John"), password_form->username_value); - EXPECT_EQ(base::ASCIIToUTF16("alpha1"), password_form->password_value); - EXPECT_THAT( - password_form->all_possible_passwords, - testing::ElementsAre(ValueElementPair(base::ASCIIToUTF16("alpha1"), - base::ASCIIToUTF16("password1")), - ValueElementPair(base::ASCIIToUTF16("alpha2"), - base::ASCIIToUTF16("password2")), - ValueElementPair(base::ASCIIToUTF16("alpha3"), - base::ASCIIToUTF16("password3")), - ValueElementPair(base::ASCIIToUTF16("alpha4"), - base::ASCIIToUTF16("password4")))); - EXPECT_EQ(base::ASCIIToUTF16("alpha1+password1, alpha2+password2, " - "alpha3+password3, alpha4+password4"), - ValueElementVectorToString(password_form->all_possible_passwords)); -} - -TEST_F(PasswordFormConversionUtilsTest, - AllPossiblePasswordsIncludeAutofilledValue) { - for (bool autofilled_value_was_modified_by_user : {false, true}) { - PasswordFormBuilder builder(kTestFormActionURL); - builder.AddTextField("username1", "John", nullptr); - builder.AddPasswordField("old-password", "autofilled_value", nullptr); - builder.AddPasswordField("new-password", "user_value", nullptr); - builder.AddSubmitButton("submit"); - std::string html = builder.ProduceHTML(); - - WebFormElement form; - LoadWebFormFromHTML(html, &form, nullptr); - WebVector<WebFormControlElement> control_elements; - form.GetFormControlElements(control_elements); - - FieldDataManager field_data_manager; - FieldPropertiesMask mask = FieldPropertiesFlags::AUTOFILLED; - if (autofilled_value_was_modified_by_user) - mask |= FieldPropertiesFlags::USER_TYPED; - field_data_manager.UpdateFieldDataMap( - control_elements[1], base::ASCIIToUTF16("autofilled_value"), mask); - field_data_manager.UpdateFieldDataMap(control_elements[2], - base::ASCIIToUTF16("user_value"), - FieldPropertiesFlags::USER_TYPED); - - std::unique_ptr<PasswordForm> password_form(CreatePasswordFormFromWebForm( - form, &field_data_manager, nullptr, nullptr)); - ASSERT_TRUE(password_form); - EXPECT_TRUE(password_form->form_has_autofilled_value); - } -} - -TEST_F(PasswordFormConversionUtilsTest, CreditCardNumberWithTypePasswordForm) { - PasswordFormBuilder builder(kTestFormActionURL); - builder.AddTextField("Credit-card-owner-name", "John Smith", nullptr); - builder.AddPasswordField("Credit-card-number", "0000 0000 0000 0000", - nullptr); - builder.AddTextField("cvc", "000", nullptr); - builder.AddSubmitButton("submit"); - std::string html = builder.ProduceHTML(); - - std::map<int, PasswordFormFieldPredictionType> predictions_positions; - predictions_positions[1] = PasswordFormFieldPredictionType::kNotPassword; - - FormsPredictionsMap predictions; - SetPredictions(html, &predictions, predictions_positions); - - std::unique_ptr<PasswordForm> password_form = - LoadHTMLAndConvertForm(html, &predictions, false); - EXPECT_TRUE(password_form); - EXPECT_TRUE(password_form->only_for_fallback); - EXPECT_EQ(base::UTF8ToUTF16("Credit-card-owner-name"), - password_form->username_element); - EXPECT_EQ(base::UTF8ToUTF16("John Smith"), password_form->username_value); - EXPECT_EQ(base::UTF8ToUTF16("Credit-card-number"), - password_form->password_element); - EXPECT_EQ(base::UTF8ToUTF16("0000 0000 0000 0000"), - password_form->password_value); -} - -TEST_F(PasswordFormConversionUtilsTest, UsernamePredictionFromServer) { - PasswordFormBuilder builder(kTestFormActionURL); - builder.AddTextField("username", "JohnSmith", nullptr); - // 'autocomplete' attribute cannot override server's prediction. - builder.AddTextField("Full name", "John A. Smith", "username"); - builder.AddPasswordField("password", "secret", nullptr); - builder.AddSubmitButton("submit"); - std::string html = builder.ProduceHTML(); - - std::map<int, PasswordFormFieldPredictionType> predictions_positions; - predictions_positions[0] = PasswordFormFieldPredictionType::kUsername; - FormsPredictionsMap predictions; - SetPredictions(html, &predictions, predictions_positions); - - base::HistogramTester histogram_tester; - std::unique_ptr<PasswordForm> password_form = - LoadHTMLAndConvertForm(html, &predictions, false); - ASSERT_TRUE(password_form); - EXPECT_EQ(base::UTF8ToUTF16("username"), password_form->username_element); - EXPECT_EQ(base::UTF8ToUTF16("JohnSmith"), password_form->username_value); - histogram_tester.ExpectUniqueSample( - "PasswordManager.UsernameDetectionMethod", - UsernameDetectionMethod::SERVER_SIDE_PREDICTION, 1); -} - -TEST_F(PasswordFormConversionUtilsTest, - UsernamePredictionFromServerToEmptyField) { - // Tests that if a form has user input and the username prediction by the - // server points to an empty field, then the prediction is ignored. - PasswordFormBuilder builder(kTestFormActionURL); - builder.AddTextField("empty-field", "", ""); // The prediction points here. - builder.AddTextField("full-name", "John A. Smith", nullptr); - builder.AddTextField("username", "JohnSmith", nullptr); - builder.AddPasswordField("password", "secret", nullptr); - builder.AddSubmitButton("submit"); - std::string html = builder.ProduceHTML(); - - std::map<int, PasswordFormFieldPredictionType> predictions_positions; - predictions_positions[0] = PasswordFormFieldPredictionType::kUsername; - FormsPredictionsMap predictions; - SetPredictions(html, &predictions, predictions_positions); - - // The password field has user input. - WebFormElement form; - LoadWebFormFromHTML(html, &form, nullptr); - WebVector<WebFormControlElement> control_elements; - form.GetFormControlElements(control_elements); - FieldDataManager field_data_manager; - field_data_manager.UpdateFieldDataMap(control_elements[3], - control_elements[3].Value().Utf16(), - FieldPropertiesFlags::USER_TYPED); - - std::unique_ptr<PasswordForm> password_form = CreatePasswordFormFromWebForm( - form, &field_data_manager, &predictions, &username_detector_cache_); - ASSERT_TRUE(password_form); - EXPECT_EQ(base::UTF8ToUTF16("username"), password_form->username_element); - EXPECT_EQ(base::UTF8ToUTF16("JohnSmith"), password_form->username_value); -} - -TEST_F(PasswordFormConversionUtilsTest, - CreditCardVerificationNumberWithTypePasswordForm) { - PasswordFormBuilder builder(kTestFormActionURL); - builder.AddTextField("Credit-card-owner-name", "John Smith", nullptr); - builder.AddTextField("Credit-card-number", "0000 0000 0000 0000", nullptr); - builder.AddPasswordField("cvc", "000", nullptr); - builder.AddSubmitButton("submit"); - std::string html = builder.ProduceHTML(); - - std::map<int, PasswordFormFieldPredictionType> predictions_positions; - predictions_positions[2] = PasswordFormFieldPredictionType::kNotPassword; - - FormsPredictionsMap predictions; - SetPredictions(html, &predictions, predictions_positions); - - std::unique_ptr<PasswordForm> password_form = - LoadHTMLAndConvertForm(html, &predictions, false); - EXPECT_TRUE(password_form); - EXPECT_TRUE(password_form->only_for_fallback); - EXPECT_EQ(base::UTF8ToUTF16("Credit-card-number"), - password_form->username_element); - EXPECT_EQ(base::UTF8ToUTF16("0000 0000 0000 0000"), - password_form->username_value); - EXPECT_EQ(base::UTF8ToUTF16("cvc"), password_form->password_element); - EXPECT_EQ(base::UTF8ToUTF16("000"), password_form->password_value); -} - -TEST_F(PasswordFormConversionUtilsTest, - CreditCardNumberWithTypePasswordFormWithAutocomplete) { - PasswordFormBuilder builder(kTestFormActionURL); - builder.AddTextField("Credit-card-owner-name", "John Smith", nullptr); - builder.AddPasswordField("Credit-card-number", "0000 0000 0000 0000", - "current-password"); - builder.AddTextField("cvc", "000", nullptr); - builder.AddSubmitButton("submit"); - std::string html = builder.ProduceHTML(); - - std::map<int, PasswordFormFieldPredictionType> predictions_positions; - predictions_positions[1] = PasswordFormFieldPredictionType::kNotPassword; - - FormsPredictionsMap predictions; - SetPredictions(html, &predictions, predictions_positions); - - std::unique_ptr<PasswordForm> password_form = - LoadHTMLAndConvertForm(html, &predictions, false); - EXPECT_TRUE(password_form); - EXPECT_FALSE(password_form->only_for_fallback); - EXPECT_EQ(base::UTF8ToUTF16("Credit-card-owner-name"), - password_form->username_element); - EXPECT_EQ(base::UTF8ToUTF16("John Smith"), password_form->username_value); - EXPECT_EQ(base::UTF8ToUTF16("Credit-card-number"), - password_form->password_element); - EXPECT_EQ(base::UTF8ToUTF16("0000 0000 0000 0000"), - password_form->password_value); -} - -TEST_F(PasswordFormConversionUtilsTest, - CreditCardVerificationNumberWithTypePasswordFormWithAutocomplete) { - PasswordFormBuilder builder(kTestFormActionURL); - builder.AddTextField("Credit-card-owner-name", "John Smith", nullptr); - builder.AddTextField("Credit-card-number", "0000 0000 0000 0000", nullptr); - builder.AddPasswordField("cvc", "000", "new-password"); - builder.AddSubmitButton("submit"); - std::string html = builder.ProduceHTML(); - - std::map<int, PasswordFormFieldPredictionType> predictions_positions; - predictions_positions[2] = PasswordFormFieldPredictionType::kNotPassword; - - FormsPredictionsMap predictions; - SetPredictions(html, &predictions, predictions_positions); - - std::unique_ptr<PasswordForm> password_form = - LoadHTMLAndConvertForm(html, &predictions, false); - ASSERT_TRUE(password_form); - EXPECT_FALSE(password_form->only_for_fallback); - EXPECT_EQ(base::UTF8ToUTF16("Credit-card-number"), - password_form->username_element); - EXPECT_EQ(base::UTF8ToUTF16("0000 0000 0000 0000"), - password_form->username_value); - EXPECT_TRUE(password_form->password_element.empty()); - EXPECT_TRUE(password_form->password_value.empty()); - EXPECT_EQ(base::UTF8ToUTF16("cvc"), password_form->new_password_element); - EXPECT_EQ(base::UTF8ToUTF16("000"), password_form->new_password_value); -} - TEST_F(PasswordFormConversionUtilsTest, IsGaiaReauthFormIgnored) { struct TestCase { const char* origin; @@ -2201,255 +253,4 @@ TEST_F(PasswordFormConversionUtilsTest, IsGaiaWithSkipSavePasswordForm) { } } -TEST_F(PasswordFormConversionUtilsTest, - IdentifyingFieldsWithoutNameOrIdAttributes) { - const char* kEmpty = nullptr; - const struct { - const char* username_fieldname; - const char* password_fieldname; - const char* new_password_fieldname; - const char* expected_username_element; - const char* expected_password_element; - const char* expected_new_password_element; - } test_cases[] = { - {"username", "password", "new_password", "username", "password", - "new_password"}, - {"username", "password", kEmpty, "username", "password", - "anonymous_new_password"}, - {"username", kEmpty, kEmpty, "username", "anonymous_password", - "anonymous_new_password"}, - {kEmpty, kEmpty, kEmpty, "anonymous_username", "anonymous_password", - "anonymous_new_password"}, - }; - - for (size_t i = 0; i < base::size(test_cases); ++i) { - SCOPED_TRACE(testing::Message() - << "Iteration " << i << ", expected_username " - << test_cases[i].expected_username_element - << ", expected_password" - << test_cases[i].expected_password_element - << ", expected_new_password " - << test_cases[i].expected_new_password_element); - - PasswordFormBuilder builder(kTestFormActionURL); - if (test_cases[i].username_fieldname == kEmpty) { - builder.AddAnonymousInputField("text"); - } else { - builder.AddTextField(test_cases[i].username_fieldname, "", kEmpty); - } - - if (test_cases[i].password_fieldname == kEmpty) { - builder.AddAnonymousInputField("password"); - } else { - builder.AddPasswordField(test_cases[i].password_fieldname, "", kEmpty); - } - - if (test_cases[i].new_password_fieldname == kEmpty) { - builder.AddAnonymousInputField("password"); - } else { - builder.AddPasswordField(test_cases[i].new_password_fieldname, "", - kEmpty); - } - std::string html = builder.ProduceHTML(); - - std::unique_ptr<PasswordForm> password_form = - LoadHTMLAndConvertForm(html, nullptr, false); - EXPECT_TRUE(password_form); - - EXPECT_FALSE(password_form->only_for_fallback); - EXPECT_EQ(base::UTF8ToUTF16(test_cases[i].expected_username_element), - password_form->username_element); - EXPECT_EQ(base::UTF8ToUTF16(test_cases[i].expected_password_element), - password_form->password_element); - EXPECT_EQ(base::UTF8ToUTF16(test_cases[i].expected_new_password_element), - password_form->new_password_element); - } -} - -TEST_F(PasswordFormConversionUtilsTest, TooManyFieldsToParseForm) { - PasswordFormBuilder builder(kTestFormActionURL); - for (size_t i = 0; i < form_util::kMaxParseableFields + 1; ++i) - builder.AddTextField("id", "value", "autocomplete"); - std::unique_ptr<PasswordForm> password_form = - LoadHTMLAndConvertForm(builder.ProduceHTML(), nullptr, false); - EXPECT_FALSE(password_form); -} - -TEST_F(PasswordFormConversionUtilsTest, OnlyCreditCardFields) { - PasswordFormBuilder builder(kTestFormActionURL); - builder.AddTextField("ccname", "johnsmith", "cc-name"); - builder.AddPasswordField("cc_security_code", "0123456789", "cc-csc"); - builder.AddSubmitButton("submit"); - std::string html = builder.ProduceHTML(); - - std::unique_ptr<PasswordForm> password_form = - LoadHTMLAndConvertForm(html, nullptr, false); - EXPECT_TRUE(password_form); - EXPECT_TRUE(password_form->only_for_fallback); - EXPECT_EQ(base::UTF8ToUTF16("ccname"), password_form->username_element); - EXPECT_EQ(base::UTF8ToUTF16("johnsmith"), password_form->username_value); - EXPECT_EQ(base::UTF8ToUTF16("cc_security_code"), - password_form->password_element); - EXPECT_EQ(base::UTF8ToUTF16("0123456789"), password_form->password_value); -} - -TEST_F(PasswordFormConversionUtilsTest, - FieldsWithAndWithoutCreditCardAttributes) { - PasswordFormBuilder builder(kTestFormActionURL); - builder.AddTextField("username", "johnsmith", nullptr); - builder.AddTextField("ccname", "john_smith", "cc-name"); - builder.AddPasswordField("cc_security_code", "0123456789", "random cc-csc"); - builder.AddPasswordField("password", "secret", nullptr); - builder.AddSubmitButton("submit"); - std::string html = builder.ProduceHTML(); - - std::unique_ptr<PasswordForm> password_form = - LoadHTMLAndConvertForm(html, nullptr, false); - - ASSERT_TRUE(password_form); - - EXPECT_FALSE(password_form->only_for_fallback); - EXPECT_EQ(base::UTF8ToUTF16("username"), password_form->username_element); - EXPECT_EQ(base::UTF8ToUTF16("johnsmith"), password_form->username_value); - EXPECT_EQ(base::UTF8ToUTF16("password"), password_form->password_element); - EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->password_value); -} - -TEST_F(PasswordFormConversionUtilsTest, ResetPasswordForm) { - // GetPassword (including HTML classifier) should process correctly forms - // without any text fields. - PasswordFormBuilder builder(kTestFormActionURL); - builder.AddPasswordField("password", "secret", nullptr); - builder.AddSubmitButton("submit"); - std::string html = builder.ProduceHTML(); - base::HistogramTester histogram_tester; - - std::unique_ptr<PasswordForm> password_form = - LoadHTMLAndConvertForm(html, nullptr, false); - - ASSERT_TRUE(password_form); - - EXPECT_TRUE(password_form->username_element.empty()); - EXPECT_TRUE(password_form->username_value.empty()); - EXPECT_EQ(base::UTF8ToUTF16("password"), password_form->password_element); - EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->password_value); - histogram_tester.ExpectUniqueSample( - "PasswordManager.UsernameDetectionMethod", - UsernameDetectionMethod::NO_USERNAME_DETECTED, 1); -} - -TEST_F(PasswordFormConversionUtilsTest, StickyPasswordType) { - PasswordFormBuilder builder(kTestFormActionURL); - builder.AddTextField("username", "johnsmith", nullptr); - builder.AddPasswordField("password", "secret", nullptr); - builder.AddSubmitButton("submit"); - std::string html = builder.ProduceHTML(); - - std::unique_ptr<PasswordForm> password_form = - LoadHTMLAndConvertForm(html, nullptr, false); - ASSERT_TRUE(password_form); - - FormData old_form_data; - ASSERT_TRUE(ExtractFormDataForFirstForm(&old_form_data)); - - // Change password field to type="text". - ExecuteJavaScriptForTests( - "document.getElementById(\"password\").type = \"text\";"); - - // Validate that - despite the change - the old password field is still - // recognized as a password field. - WebFormElement new_form; - GetFirstForm(&new_form); - std::unique_ptr<PasswordForm> new_password_form = - CreatePasswordFormFromWebForm(new_form, nullptr, nullptr, - &username_detector_cache_); - ASSERT_TRUE(new_password_form); - - EXPECT_EQ(*password_form, *new_password_form); - - FormData new_form_data; - ASSERT_TRUE(ExtractFormDataForFirstForm(&new_form_data)); - - EXPECT_EQ(old_form_data, new_form_data); -} - -// Check that Chrome remembers the value typed by the user in cases when it gets -// overridden by the page. -TEST_F(PasswordFormConversionUtilsTest, TypedValuePreserved) { - PasswordFormBuilder builder(kTestFormActionURL); - builder.AddTextField("fine", "", "username"); - builder.AddPasswordField("mangled", "", "current-password"); - builder.AddTextField("completed_for_user", "", nullptr); - std::string html = builder.ProduceHTML(); - - WebFormElement form; - LoadWebFormFromHTML(html, &form, nullptr); - - FieldDataManager field_data_manager; - WebVector<WebFormControlElement> control_elements; - form.GetFormControlElements(control_elements); - - ASSERT_EQ(3u, control_elements.size()); - ASSERT_EQ("fine", control_elements[0].NameForAutofill().Utf8()); - control_elements[0].SetAutofillValue("same_value"); - field_data_manager.UpdateFieldDataMap(control_elements[0], - control_elements[0].Value().Utf16(), - FieldPropertiesFlags::USER_TYPED); - - ASSERT_EQ("mangled", control_elements[1].NameForAutofill().Utf8()); - control_elements[1].SetAutofillValue("mangled_value"); - field_data_manager.UpdateFieldDataMap(control_elements[1], - base::UTF8ToUTF16("original_value"), - FieldPropertiesFlags::USER_TYPED); - - ASSERT_EQ("completed_for_user", control_elements[2].NameForAutofill().Utf8()); - control_elements[2].SetAutofillValue("email@gmail.com"); - field_data_manager.UpdateFieldDataMap(control_elements[2], - base::UTF8ToUTF16("email"), - FieldPropertiesFlags::USER_TYPED); - - std::unique_ptr<PasswordForm> password_form = CreatePasswordFormFromWebForm( - form, &field_data_manager, nullptr, nullptr); - - ASSERT_TRUE(password_form); - - EXPECT_EQ(base::UTF8ToUTF16("same_value"), password_form->username_value); - EXPECT_EQ(base::UTF8ToUTF16("original_value"), password_form->password_value); - - ASSERT_EQ(3u, password_form->form_data.fields.size()); - - EXPECT_EQ(base::UTF8ToUTF16("same_value"), - password_form->form_data.fields[0].value); - EXPECT_EQ(base::string16(), password_form->form_data.fields[0].typed_value); - - EXPECT_EQ(base::UTF8ToUTF16("mangled_value"), - password_form->form_data.fields[1].value); - EXPECT_EQ(base::UTF8ToUTF16("original_value"), - password_form->form_data.fields[1].typed_value); - - EXPECT_EQ(base::UTF8ToUTF16("email@gmail.com"), - password_form->form_data.fields[2].value); - EXPECT_EQ(base::string16(), password_form->form_data.fields[2].typed_value); -} - -// Check that non-text fields are ignored. -TEST_F(PasswordFormConversionUtilsTest, NonTextFields) { - PasswordFormBuilder builder(kTestFormActionURL); - // Avoid calling the text fields anything related to "username" to prevent the - // local HTML classifier from influencing the test result. - builder.AddTextField("textField", "", ""); - builder.AddFieldWithType("radioInput", "radio"); - builder.AddPasswordField("password", "", ""); - std::string html = builder.ProduceHTML(); - - WebFormElement form; - LoadWebFormFromHTML(html, &form, nullptr); - - std::unique_ptr<PasswordForm> password_form = - CreatePasswordFormFromWebForm(form, nullptr, nullptr, nullptr); - - ASSERT_TRUE(password_form); - EXPECT_EQ(base::UTF8ToUTF16("textField"), password_form->username_element); -} - } // namespace autofill diff --git a/chromium/components/autofill/content/renderer/password_generation_agent.cc b/chromium/components/autofill/content/renderer/password_generation_agent.cc index 5aafec2069b..e5095c12eee 100644 --- a/chromium/components/autofill/content/renderer/password_generation_agent.cc +++ b/chromium/components/autofill/content/renderer/password_generation_agent.cc @@ -307,15 +307,22 @@ void PasswordGenerationAgent::UserTriggeredGeneratePassword( UserTriggeredGeneratePasswordCallback callback) { if (SetUpUserTriggeredGeneration()) { LogMessage(Logger::STRING_GENERATION_RENDERER_SHOW_MANUAL_GENERATION_POPUP); + // If the field is not |type=password|, the list of suggestions + // should not be populated with passwords to avoid filling them in a + // clear-text field. + // |IsPasswordFieldForAutofill()| is deliberately not used. + bool is_generation_element_password_type = + current_generation_item_->generation_element_.IsPasswordField(); autofill::password_generation::PasswordGenerationUIData password_generation_ui_data( - render_frame()->GetRenderView()->ElementBoundsInWindow( + render_frame()->ElementBoundsInWindow( current_generation_item_->generation_element_), current_generation_item_->generation_element_.MaxLength(), current_generation_item_->generation_element_.NameForAutofill() .Utf16(), current_generation_item_->generation_element_ .UniqueRendererFormControlId(), + is_generation_element_password_type, GetTextDirectionForElement( current_generation_item_->generation_element_), current_generation_item_->form_); @@ -511,15 +518,22 @@ void PasswordGenerationAgent::AutomaticGenerationAvailable() { DCHECK(current_generation_item_); DCHECK(!current_generation_item_->generation_element_.IsNull()); LogMessage(Logger::STRING_GENERATION_RENDERER_AUTOMATIC_GENERATION_AVAILABLE); + // If the field is not |type=password|, the list of suggestions + // should not be populated with passwordS to avoid filling them in a + // clear-text field. + // |IsPasswordFieldForAutofill()| is deliberately not used. + bool is_generation_element_password_type = + current_generation_item_->generation_element_.IsPasswordField(); autofill::password_generation::PasswordGenerationUIData password_generation_ui_data( - render_frame()->GetRenderView()->ElementBoundsInWindow( + render_frame()->ElementBoundsInWindow( current_generation_item_->generation_element_), current_generation_item_->generation_element_.MaxLength(), current_generation_item_->generation_element_.NameForAutofill() .Utf16(), current_generation_item_->generation_element_ .UniqueRendererFormControlId(), + is_generation_element_password_type, GetTextDirectionForElement( current_generation_item_->generation_element_), current_generation_item_->form_); @@ -532,9 +546,8 @@ void PasswordGenerationAgent::ShowEditingPopup() { if (!render_frame()) return; - gfx::RectF bounding_box = - render_frame()->GetRenderView()->ElementBoundsInWindow( - current_generation_item_->generation_element_); + gfx::RectF bounding_box = render_frame()->ElementBoundsInWindow( + current_generation_item_->generation_element_); std::unique_ptr<PasswordForm> password_form = CreatePasswordFormToPresave(); DCHECK(password_form); diff --git a/chromium/components/autofill/content/renderer/provisionally_saved_password_form.cc b/chromium/components/autofill/content/renderer/provisionally_saved_password_form.cc deleted file mode 100644 index 376eddff494..00000000000 --- a/chromium/components/autofill/content/renderer/provisionally_saved_password_form.cc +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2017 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 "components/autofill/content/renderer/provisionally_saved_password_form.h" - -#include <utility> - -namespace autofill { - -ProvisionallySavedPasswordForm::ProvisionallySavedPasswordForm() = default; - -ProvisionallySavedPasswordForm::~ProvisionallySavedPasswordForm() = default; - -void ProvisionallySavedPasswordForm::Set( - std::unique_ptr<PasswordForm> password_form, - const blink::WebFormElement& form_element, - const blink::WebInputElement& input_element) { - password_form_ = std::move(password_form); - form_element_ = form_element; - input_element_ = input_element; -} - -void ProvisionallySavedPasswordForm::Reset() { - password_form_.reset(); - form_element_.Reset(); - input_element_.Reset(); -} - -bool ProvisionallySavedPasswordForm::IsSet() const { - return static_cast<bool>(password_form_); -} - -bool ProvisionallySavedPasswordForm::IsPasswordValid() const { - return IsSet() && !(password_form_->password_value.empty() && - password_form_->new_password_value.empty()); -} - -void ProvisionallySavedPasswordForm::SetSubmissionIndicatorEvent( - mojom::SubmissionIndicatorEvent event) { - if (password_form_) { - password_form_->submission_event = event; - password_form_->form_data.submission_event = event; - } -} - -} // namespace autofill diff --git a/chromium/components/autofill/content/renderer/provisionally_saved_password_form.h b/chromium/components/autofill/content/renderer/provisionally_saved_password_form.h deleted file mode 100644 index 89e9b11fa26..00000000000 --- a/chromium/components/autofill/content/renderer/provisionally_saved_password_form.h +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2017 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. - -#ifndef COMPONENTS_AUTOFILL_CONTENT_RENDERER_PROVISIONALLY_SAVED_PASSWORD_FORM_H_ -#define COMPONENTS_AUTOFILL_CONTENT_RENDERER_PROVISIONALLY_SAVED_PASSWORD_FORM_H_ - -#include <memory> - -#include "base/macros.h" -#include "components/autofill/core/common/mojom/autofill_types.mojom.h" -#include "components/autofill/core/common/password_form.h" -#include "third_party/blink/public/web/web_input_element.h" - -namespace autofill { - -struct PasswordForm; - -// Represents a possibly submitted password form. -class ProvisionallySavedPasswordForm { - public: - ProvisionallySavedPasswordForm(); - ~ProvisionallySavedPasswordForm(); - - // Sets the PasswordForm and web elements that were used in the PasswordForm - // update. - void Set(std::unique_ptr<PasswordForm> password_form, - const blink::WebFormElement& form_element, - const blink::WebInputElement& input_element); - void Reset(); - - // Returns true if the instance has |password_form_| set, but the actual - // password data may be invalid (e.g. empty username or password). - bool IsSet() const; - // Returns true if |password_form_| has enough information that it is likely - // filled out. - bool IsPasswordValid() const; - - const PasswordForm& password_form() const { - DCHECK(IsSet()); - return *password_form_; - } - blink::WebFormElement& form_element() { return form_element_; } - blink::WebInputElement& input_element() { return input_element_; } - - void SetSubmissionIndicatorEvent(mojom::SubmissionIndicatorEvent event); - - private: - std::unique_ptr<PasswordForm> password_form_; - // Last used WebFormElement for the PasswordForm submission. Can be null if - // the form is unowned. - blink::WebFormElement form_element_; - // Last used WebInputElement which led to the PasswordForm update. Can be null - // if the user has performed a form submission (via a button, for example). - blink::WebInputElement input_element_; - - DISALLOW_COPY_AND_ASSIGN(ProvisionallySavedPasswordForm); -}; - -} // namespace autofill - -#endif // COMPONENTS_AUTOFILL_CONTENT_RENDERER_PROVISIONALLY_SAVED_PASSWORD_FORM_H_ diff --git a/chromium/components/autofill/content/renderer/renderer_save_password_progress_logger_unittest.cc b/chromium/components/autofill/content/renderer/renderer_save_password_progress_logger_unittest.cc index badd66a6a3b..1cce757d9bb 100644 --- a/chromium/components/autofill/content/renderer/renderer_save_password_progress_logger_unittest.cc +++ b/chromium/components/autofill/content/renderer/renderer_save_password_progress_logger_unittest.cc @@ -8,7 +8,9 @@ #include "base/run_loop.h" #include "base/test/task_environment.h" #include "components/autofill/content/common/mojom/autofill_driver.mojom.h" -#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/receiver.h" +#include "mojo/public/cpp/bindings/remote.h" #include "testing/gtest/include/gtest/gtest.h" namespace autofill { @@ -19,14 +21,12 @@ const char kTestText[] = "test"; class FakeContentPasswordManagerDriver : public mojom::PasswordManagerDriver { public: - FakeContentPasswordManagerDriver() - : called_record_save_(false), binding_(this) {} + FakeContentPasswordManagerDriver() : called_record_save_(false) {} ~FakeContentPasswordManagerDriver() override {} - mojom::PasswordManagerDriverPtr CreateInterfacePtrAndBind() { - mojom::PasswordManagerDriverPtr ptr; - binding_.Bind(mojo::MakeRequest(&ptr)); - return ptr; + mojo::PendingRemote<mojom::PasswordManagerDriver> + CreatePendingRemoteAndBind() { + return receiver_.BindNewPipeAndPassRemote(); } bool GetLogMessage(std::string* log) { @@ -55,8 +55,8 @@ class FakeContentPasswordManagerDriver : public mojom::PasswordManagerDriver { void HideManualFallbackForSaving() override {} - void SameDocumentNavigation( - const autofill::PasswordForm& password_form) override {} + void SameDocumentNavigation(autofill::mojom::SubmissionIndicatorEvent + submission_indication_event) override {} void ShowPasswordSuggestions(base::i18n::TextDirection text_direction, const base::string16& typed_username, @@ -88,7 +88,7 @@ class FakeContentPasswordManagerDriver : public mojom::PasswordManagerDriver { // Records data received via RecordSavePasswordProgress() call. base::Optional<std::string> log_; - mojo::Binding<mojom::PasswordManagerDriver> binding_; + mojo::Receiver<mojom::PasswordManagerDriver> receiver_{this}; }; class TestLogger : public RendererSavePasswordProgressLogger { @@ -104,9 +104,9 @@ class TestLogger : public RendererSavePasswordProgressLogger { TEST(RendererSavePasswordProgressLoggerTest, SendLog) { base::test::SingleThreadTaskEnvironment task_environment; FakeContentPasswordManagerDriver fake_driver; - mojom::PasswordManagerDriverPtr driver_ptr = - fake_driver.CreateInterfacePtrAndBind(); - TestLogger logger(driver_ptr.get()); + mojo::Remote<mojom::PasswordManagerDriver> driver_remote( + fake_driver.CreatePendingRemoteAndBind()); + TestLogger logger(driver_remote.get()); logger.SendLog(kTestText); base::RunLoop().RunUntilIdle(); diff --git a/chromium/components/autofill/core/browser/BUILD.gn b/chromium/components/autofill/core/browser/BUILD.gn index 44b052554fe..ea02a8035e7 100644 --- a/chromium/components/autofill/core/browser/BUILD.gn +++ b/chromium/components/autofill/core/browser/BUILD.gn @@ -56,8 +56,6 @@ jumbo_static_library("browser") { "autofill_handler_proxy.h", "autofill_ie_toolbar_import_win.cc", "autofill_ie_toolbar_import_win.h", - "autofill_internals_service.cc", - "autofill_internals_service.h", "autofill_manager.cc", "autofill_manager.h", "autofill_manager_test_delegate.h", @@ -145,12 +143,11 @@ jumbo_static_library("browser") { "geo/state_names.h", "geo/subkey_requester.cc", "geo/subkey_requester.h", - "logging/log_buffer.cc", - "logging/log_buffer.h", "logging/log_buffer_submitter.cc", "logging/log_buffer_submitter.h", "logging/log_manager.cc", "logging/log_manager.h", + "logging/log_protobufs.h", "logging/log_receiver.h", "logging/log_router.cc", "logging/log_router.h", @@ -197,6 +194,8 @@ jumbo_static_library("browser") { "payments/strike_database_integrator_base.h", "payments/strike_database_integrator_test_strike_database.cc", "payments/strike_database_integrator_test_strike_database.h", + "payments/upi_vpa_save_manager.cc", + "payments/upi_vpa_save_manager.h", "personal_data_manager.cc", "personal_data_manager.h", "personal_data_manager_observer.h", @@ -296,8 +295,10 @@ jumbo_static_library("browser") { "ui/mobile_label_formatter.h", "ui/payments/card_expiration_date_fix_flow_view_delegate_mobile.cc", "ui/payments/card_expiration_date_fix_flow_view_delegate_mobile.h", - "ui/payments/card_name_fix_flow_view_delegate_mobile.cc", - "ui/payments/card_name_fix_flow_view_delegate_mobile.h", + "ui/payments/card_name_fix_flow_controller.h", + "ui/payments/card_name_fix_flow_controller_impl.cc", + "ui/payments/card_name_fix_flow_controller_impl.h", + "ui/payments/card_name_fix_flow_view.h", ] } @@ -318,6 +319,8 @@ jumbo_static_library("browser") { "autofill_policy_handler.h", "payments/credit_card_fido_authenticator.cc", "payments/credit_card_fido_authenticator.h", + "payments/fido_authentication_strike_database.cc", + "payments/fido_authentication_strike_database.h", ] } @@ -441,6 +444,8 @@ jumbo_static_library("test_support") { "test_autofill_profile_validator_delayed.h", "test_autofill_provider.cc", "test_autofill_provider.h", + "test_autofill_tick_clock.cc", + "test_autofill_tick_clock.h", "test_event_waiter.h", "test_form_data_importer.cc", "test_form_data_importer.h", @@ -481,6 +486,7 @@ jumbo_static_library("test_support") { "//testing/gtest", "//third_party/libaddressinput:test_support", "//third_party/libaddressinput:util", + "//third_party/re2:re2", "//ui/accessibility", "//ui/gfx:test_support", "//ui/gfx/geometry", @@ -532,7 +538,6 @@ source_set("unit_tests") { "autofill_experiments_unittest.cc", "autofill_external_delegate_unittest.cc", "autofill_ie_toolbar_import_win_unittest.cc", - "autofill_internals_service_unittest.cc", "autofill_manager_unittest.cc", "autofill_merge_unittest.cc", "autofill_metrics_unittest.cc", @@ -565,7 +570,6 @@ source_set("unit_tests") { "geo/phone_number_i18n_unittest.cc", "geo/subkey_requester_unittest.cc", "logging/log_buffer_submitter_unittest.cc", - "logging/log_buffer_unittest.cc", "logging/log_manager_unittest.cc", "logging/log_router_unittest.cc", "payments/credit_card_access_manager_unittest.cc", @@ -603,6 +607,7 @@ source_set("unit_tests") { sources += [ "autofill_assistant_unittest.cc", "ui/mobile_label_formatter_unittest.cc", + "ui/payments/card_name_fix_flow_controller_impl_unittest.cc", ] } diff --git a/chromium/components/autofill/core/browser/autocomplete_history_manager_unittest.cc b/chromium/components/autofill/core/browser/autocomplete_history_manager_unittest.cc index dd8098f25d8..973ecf62bb7 100644 --- a/chromium/components/autofill/core/browser/autocomplete_history_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/autocomplete_history_manager_unittest.cc @@ -150,7 +150,7 @@ class AutocompleteHistoryManagerTest : public testing::Test { date_last_used); } - base::test::TaskEnvironment task_environment_; + base::test::SingleThreadTaskEnvironment task_environment_; scoped_refptr<MockWebDataService> web_data_service_; std::unique_ptr<AutocompleteHistoryManager> autocomplete_manager_; std::unique_ptr<PrefService> prefs_; diff --git a/chromium/components/autofill/core/browser/autofill_address_util.cc b/chromium/components/autofill/core/browser/autofill_address_util.cc index 9908b219b85..e621752b495 100644 --- a/chromium/components/autofill/core/browser/autofill_address_util.cc +++ b/chromium/components/autofill/core/browser/autofill_address_util.cc @@ -154,38 +154,4 @@ void GetAddressComponents(const std::string& country_code, } } -// Sets data related to the country <select>. -void SetCountryData(const PersonalDataManager& manager, - base::DictionaryValue* localized_strings, - const std::string& ui_language_code) { - autofill::CountryComboboxModel model; - model.SetCountries(manager, base::Callback<bool(const std::string&)>(), - ui_language_code); - const std::vector<std::unique_ptr<autofill::AutofillCountry>>& countries = - model.countries(); - localized_strings->SetString("defaultCountryCode", - countries.front()->country_code()); - - // An ordered list of options to show in the <select>. - auto country_list = std::make_unique<base::ListValue>(); - for (size_t i = 0; i < countries.size(); ++i) { - auto option_details = std::make_unique<base::DictionaryValue>(); - option_details->SetString("name", model.GetItemAt(i)); - option_details->SetString( - "value", countries[i] ? countries[i]->country_code() : "separator"); - country_list->Append(std::move(option_details)); - } - localized_strings->Set("autofillCountrySelectList", std::move(country_list)); - - auto default_country_components = std::make_unique<base::ListValue>(); - std::string default_country_language_code; - GetAddressComponents(countries.front()->country_code(), ui_language_code, - default_country_components.get(), - &default_country_language_code); - localized_strings->Set("autofillDefaultCountryComponents", - std::move(default_country_components)); - localized_strings->SetString("autofillDefaultCountryLanguageCode", - default_country_language_code); -} - } // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_address_util.h b/chromium/components/autofill/core/browser/autofill_address_util.h index c962f8db10b..1e8b93f76d0 100644 --- a/chromium/components/autofill/core/browser/autofill_address_util.h +++ b/chromium/components/autofill/core/browser/autofill_address_util.h @@ -75,11 +75,6 @@ void GetAddressComponents(const std::string& country_code, base::ListValue* address_components, std::string* components_language_code); -// Sets data related to the country combobox. -void SetCountryData(const PersonalDataManager& manager, - base::DictionaryValue* localized_strings, - const std::string& ui_language_code); - } // namespace autofill #endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_ADDRESS_UTIL_H_ diff --git a/chromium/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.css b/chromium/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.css index 8dcdccf06e3..de7620a179d 100644 --- a/chromium/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.css +++ b/chromium/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.css @@ -66,6 +66,10 @@ background-color: #FFECB3; } +.log-entry[scope='AbortParsing'] { + background-color: #FFCDD2; +} + .log-entry[scope='Filling'] { background-color: #D1C4E9; } diff --git a/chromium/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals_ios.html b/chromium/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals_ios.html new file mode 100644 index 00000000000..f1675379114 --- /dev/null +++ b/chromium/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals_ios.html @@ -0,0 +1,51 @@ +<!doctype html> +<html> +<head> +<!-- Copyright 2019 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. --> +<meta charset="utf-8"> + +<!-- TODO(crbug.com/487000): Remove this entire html file once the following is +injected by web. --> +<script src="chrome://resources/js/ios/web_ui.js"></script> + +<script src="chrome://resources/js/util.js"></script> +<script src="autofill_and_password_manager_internals.js"></script> +<link rel="stylesheet" href="chrome://resources/css/chrome_shared.css"> +<link rel="stylesheet" href="autofill_and_password_manager_internals.css"> +</head> +<body> +<h1 id="h1-title"></h1> +<div id="logging-note"></div> +<div id="logging-note-incognito"></div> +<div id="version-info"> + <table> + <tr> + <td class="label">Version:</td> + <td class="version"><span>$i18n{version}</span> + (<span>$i18n{official}</span>) + <span>$i18n{version_modifier}</span></td> + </tr> + <tr> + <td class="label">Revision:</td> + <td class="version"><span>$i18n{cl}</span></td> + </tr> + <tr> + <td class="label">User Agent:</td> + <td class="version"><span>$i18n{useragent}</span></td> + </tr> + <tr> + <td class="label">App Locale:</td> + <td class="version"><span>$i18n{app_locale}</span></td> + </tr> + <tr> + <td class="label">Variations:</td> + <td class="version" id="variations-list"></td> + </tr> + </table> +</div> +<div id="log-entries"> +</div> +</body> +</html> diff --git a/chromium/components/autofill/core/browser/autofill_assistant_unittest.cc b/chromium/components/autofill/core/browser/autofill_assistant_unittest.cc index 95b0e1aef02..a47451ca2e5 100644 --- a/chromium/components/autofill/core/browser/autofill_assistant_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_assistant_unittest.cc @@ -148,7 +148,7 @@ class AutofillAssistantTest : public testing::Test { return static_cast<CardUnmaskDelegate*>(full_card_request); } - base::test::TaskEnvironment task_environment_; + base::test::SingleThreadTaskEnvironment task_environment_; TestAutofillClient autofill_client_; testing::NiceMock<TestAutofillDriver> autofill_driver_; TestPersonalDataManager pdm_; diff --git a/chromium/components/autofill/core/browser/autofill_client.cc b/chromium/components/autofill/core/browser/autofill_client.cc index 98d69be2b43..aaa1e239912 100644 --- a/chromium/components/autofill/core/browser/autofill_client.cc +++ b/chromium/components/autofill/core/browser/autofill_client.cc @@ -23,4 +23,8 @@ LogManager* AutofillClient::GetLogManager() const { return nullptr; } +bool AutofillClient::CloseWebauthnOfferDialog() { + return false; +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_client.h b/chromium/components/autofill/core/browser/autofill_client.h index 09e305852ec..2ed56cfc478 100644 --- a/chromium/components/autofill/core/browser/autofill_client.h +++ b/chromium/components/autofill/core/browser/autofill_client.h @@ -6,6 +6,7 @@ #define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_CLIENT_H_ #include <memory> +#include <set> #include <string> #include <vector> @@ -15,6 +16,7 @@ #include "base/strings/string16.h" #include "base/values.h" #include "build/build_config.h" +#include "components/autofill/core/browser/payments/legal_message_line.h" #include "components/autofill/core/browser/payments/risk_data_loader.h" #include "components/autofill/core/browser/ui/popup_types.h" #include "components/security_state/core/security_state.h" @@ -282,11 +284,11 @@ class AutofillClient : public RiskDataLoader { virtual void ShowLocalCardMigrationDialog( base::OnceClosure show_migration_dialog_closure) = 0; - // Shows a dialog with the given |legal_message| and the |user_email|. Runs - // |start_migrating_cards_callback| if the user would like the selected cards - // in the |migratable_credit_cards| to be uploaded to cloud. + // Shows a dialog with the given |legal_message_lines| and the |user_email|. + // Runs |start_migrating_cards_callback| if the user would like the selected + // cards in the |migratable_credit_cards| to be uploaded to cloud. virtual void ConfirmMigrateLocalCardToCloud( - std::unique_ptr<base::DictionaryValue> legal_message, + const LegalMessageLines& legal_message_lines, const std::string& user_email, const std::vector<MigratableCreditCard>& migratable_credit_cards, LocalCardMigrationCallback start_migrating_cards_callback) = 0; @@ -304,13 +306,33 @@ class AutofillClient : public RiskDataLoader { const std::vector<MigratableCreditCard>& migratable_credit_cards, MigrationDeleteCardCallback delete_local_card_callback) = 0; +#if !defined(OS_ANDROID) && !defined(OS_IOS) + // Will show a dialog indicating the card verification is in progress. It is + // shown after verification starts only if the WebAuthn is enabled. + // Implemented only on desktop. + virtual void ShowVerifyPendingDialog( + base::OnceClosure cancel_card_verification_callback) = 0; + + // Close the verify pending dialog once the card verificiation is completed or + // verification falls back to CVC. + virtual void CloseVerifyPendingDialog() = 0; +#endif + // Will show a dialog offering the option to use device's platform // authenticator in the future instead of CVC to verify the card being - // unmasked. Runs |callback| is the OK button or the cancel button in the + // unmasked. Runs |callback| if the OK button or the cancel button in the // dialog is clicked. This is only implemented on desktop. virtual void ShowWebauthnOfferDialog( WebauthnOfferDialogCallback callback) = 0; + // Will close the WebAuthn offer dialog. Returns true if dialog was visible + // and has been closed. Implemented only on desktop. + virtual bool CloseWebauthnOfferDialog(); + + // Will update the WebAuthn offer dialog content to the error state. + // Implemented only on desktop. + virtual void UpdateWebauthnOfferDialogWithError() {} + // Runs |callback| if the |profile| should be imported as personal data. virtual void ConfirmSaveAutofillProfile(const AutofillProfile& profile, base::OnceClosure callback) = 0; @@ -326,11 +348,14 @@ class AutofillClient : public RiskDataLoader { AutofillClient::SaveCreditCardOptions options, LocalSaveCardPromptCallback callback) = 0; -#if defined(OS_ANDROID) +#if defined(OS_ANDROID) || defined(OS_IOS) // Display the cardholder name fix flow prompt and run the |callback| if // the card should be uploaded to payments with updated name from the user. virtual void ConfirmAccountNameFixFlow( base::OnceCallback<void(const base::string16&)> callback) = 0; +#endif // defined(OS_ANDROID) || defined(OS_IOS) + +#if defined(OS_ANDROID) // Display the expiration date fix flow prompt with the |card| details // and run the |callback| if the card should be uploaded to payments with // updated expiration date from the user. @@ -341,8 +366,8 @@ class AutofillClient : public RiskDataLoader { #endif // defined(OS_ANDROID) // Runs |callback| once the user makes a decision with respect to the - // offer-to-save prompt. Displays the contents of |legal_message| to the user. - // Displays a cardholder name textfield in the bubble if + // offer-to-save prompt. Displays the contents of |legal_message_lines| + // to the user. Displays a cardholder name textfield in the bubble if // |options.should_request_name_from_user| is true. Displays // a pair of expiration date dropdowns in the bubble if // |should_request_expiration_date_from_user| is true. On desktop, shows the @@ -352,7 +377,7 @@ class AutofillClient : public RiskDataLoader { // not offer to save at all. virtual void ConfirmSaveCreditCardToCloud( const CreditCard& card, - std::unique_ptr<base::DictionaryValue> legal_message, + const LegalMessageLines& legal_message_lines, SaveCreditCardOptions options, UploadSaveCardPromptCallback callback) = 0; diff --git a/chromium/components/autofill/core/browser/autofill_data_util.cc b/chromium/components/autofill/core/browser/autofill_data_util.cc index 02d4f378146..35620873ba7 100644 --- a/chromium/components/autofill/core/browser/autofill_data_util.cc +++ b/chromium/components/autofill/core/browser/autofill_data_util.cc @@ -55,12 +55,6 @@ const PaymentRequestData kPaymentRequestData[]{ {autofill::kVisaCard, "visa", IDR_AUTOFILL_CC_VISA, IDS_AUTOFILL_CC_VISA}, }; -#if BUILDFLAG(GOOGLE_CHROME_BRANDING) -const PaymentRequestData kGooglePayBrandingRequestData = { - "googlePay", "googlePay", IDR_AUTOFILL_GOOGLE_PAY, - IDS_AUTOFILL_CC_GOOGLE_PAY}; -#endif // BUILDFLAG(GOOGLE_CHROME_BRANDING) - const PaymentRequestData kGenericPaymentRequestData = { autofill::kGenericCard, "generic", IDR_AUTOFILL_CC_GENERIC, IDS_AUTOFILL_CC_GENERIC}; @@ -464,70 +458,12 @@ base::string16 JoinNameParts(base::StringPiece16 given, return base::JoinString(full_name, base::ASCIIToUTF16(separator)); } -bool ProfileMatchesFullName(base::StringPiece16 full_name, - const autofill::AutofillProfile& profile) { - const base::string16 kSpace = base::ASCIIToUTF16(" "); - const base::string16 kPeriodSpace = base::ASCIIToUTF16(". "); - - // First Last - base::string16 candidate = profile.GetRawInfo(autofill::NAME_FIRST) + kSpace + - profile.GetRawInfo(autofill::NAME_LAST); - if (!full_name.compare(candidate)) { - return true; - } - - // First Middle Last - candidate = profile.GetRawInfo(autofill::NAME_FIRST) + kSpace + - profile.GetRawInfo(autofill::NAME_MIDDLE) + kSpace + - profile.GetRawInfo(autofill::NAME_LAST); - if (!full_name.compare(candidate)) { - return true; - } - - // First M Last - candidate = profile.GetRawInfo(autofill::NAME_FIRST) + kSpace + - profile.GetRawInfo(autofill::NAME_MIDDLE_INITIAL) + kSpace + - profile.GetRawInfo(autofill::NAME_LAST); - if (!full_name.compare(candidate)) { - return true; - } - - // First M. Last - candidate = profile.GetRawInfo(autofill::NAME_FIRST) + kSpace + - profile.GetRawInfo(autofill::NAME_MIDDLE_INITIAL) + kPeriodSpace + - profile.GetRawInfo(autofill::NAME_LAST); - if (!full_name.compare(candidate)) { - return true; - } - - // Last First - candidate = profile.GetRawInfo(autofill::NAME_LAST) + kSpace + - profile.GetRawInfo(autofill::NAME_FIRST); - if (!full_name.compare(candidate)) { - return true; - } - - // LastFirst - candidate = profile.GetRawInfo(autofill::NAME_LAST) + - profile.GetRawInfo(autofill::NAME_FIRST); - if (!full_name.compare(candidate)) { - return true; - } - - return false; -} - const PaymentRequestData& GetPaymentRequestData( const std::string& issuer_network) { for (const PaymentRequestData& data : kPaymentRequestData) { if (issuer_network == data.issuer_network) return data; } -#if BUILDFLAG(GOOGLE_CHROME_BRANDING) - if (issuer_network == kGooglePayBrandingRequestData.issuer_network) { - return kGooglePayBrandingRequestData; - } -#endif // BUILDFLAG(GOOGLE_CHROME_BRANDING) return kGenericPaymentRequestData; } diff --git a/chromium/components/autofill/core/browser/autofill_data_util.h b/chromium/components/autofill/core/browser/autofill_data_util.h index a6060345333..d99dc25369e 100644 --- a/chromium/components/autofill/core/browser/autofill_data_util.h +++ b/chromium/components/autofill/core/browser/autofill_data_util.h @@ -96,11 +96,6 @@ base::string16 JoinNameParts(base::StringPiece16 given, base::StringPiece16 middle, base::StringPiece16 family); -// Returns true iff |full_name| is a concatenation of some combination of the -// first/middle/last (incl. middle initial) in |profile|. -bool ProfileMatchesFullName(base::StringPiece16 full_name, - const autofill::AutofillProfile& profile); - // Returns the Payment Request API basic card payment spec data for the provided // autofill credit card |network|. Will set the network and the icon to // "generic" for any unrecognized type. diff --git a/chromium/components/autofill/core/browser/autofill_data_util_unittest.cc b/chromium/components/autofill/core/browser/autofill_data_util_unittest.cc index 7b1ea28d9fa..2646b113750 100644 --- a/chromium/components/autofill/core/browser/autofill_data_util_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_data_util_unittest.cc @@ -233,33 +233,6 @@ INSTANTIATE_TEST_SUITE_P( // Has a middle-name, too unusual )); -TEST(AutofillDataUtilTest, ProfileMatchesFullName) { - autofill::AutofillProfile profile; - autofill::test::SetProfileInfo( - &profile, "First", "Middle", "Last", "fml@example.com", "Acme inc", - "123 Main", "Apt 2", "Laredo", "TX", "77300", "US", "832-555-1000"); - - EXPECT_TRUE(ProfileMatchesFullName(base::UTF8ToUTF16("First Last"), profile)); - - EXPECT_TRUE( - ProfileMatchesFullName(base::UTF8ToUTF16("First Middle Last"), profile)); - - EXPECT_TRUE( - ProfileMatchesFullName(base::UTF8ToUTF16("First M Last"), profile)); - - EXPECT_TRUE( - ProfileMatchesFullName(base::UTF8ToUTF16("First M. Last"), profile)); - - EXPECT_TRUE( - ProfileMatchesFullName(base::UTF8ToUTF16("Last First"), profile)); - - EXPECT_TRUE( - ProfileMatchesFullName(base::UTF8ToUTF16("LastFirst"), profile)); - - EXPECT_FALSE( - ProfileMatchesFullName(base::UTF8ToUTF16("Kirby Puckett"), profile)); -} - struct ValidCountryCodeTestCase { std::string country_code; bool expected_result; diff --git a/chromium/components/autofill/core/browser/autofill_download_manager.cc b/chromium/components/autofill/core/browser/autofill_download_manager.cc index 4513830064b..47f0b71fd95 100644 --- a/chromium/components/autofill/core/browser/autofill_download_manager.cc +++ b/chromium/components/autofill/core/browser/autofill_download_manager.cc @@ -16,6 +16,7 @@ #include "base/metrics/field_trial_params.h" #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" +#include "base/numerics/ranges.h" #include "base/numerics/safe_conversions.h" #include "base/rand_util.h" #include "base/strings/strcat.h" @@ -24,11 +25,10 @@ #include "base/strings/stringprintf.h" #include "base/threading/thread_task_runner_handle.h" #include "components/autofill/core/browser/autofill_driver.h" -#include "components/autofill/core/browser/autofill_internals_service.h" #include "components/autofill/core/browser/autofill_metrics.h" #include "components/autofill/core/browser/form_structure.h" -#include "components/autofill/core/browser/logging/log_buffer.h" #include "components/autofill/core/browser/logging/log_manager.h" +#include "components/autofill/core/browser/logging/log_protobufs.h" #include "components/autofill/core/browser/proto/legacy_proto_bridge.h" #include "components/autofill/core/browser/proto/server.pb.h" #include "components/autofill/core/common/autofill_clock.h" @@ -37,6 +37,8 @@ #include "components/autofill/core/common/autofill_internals/logging_scope.h" #include "components/autofill/core/common/autofill_prefs.h" #include "components/autofill/core/common/autofill_switches.h" +#include "components/autofill/core/common/autofill_tick_clock.h" +#include "components/autofill/core/common/logging/log_buffer.h" #include "components/autofill/core/common/mojom/autofill_types.mojom.h" #include "components/google/core/common/google_util.h" #include "components/history/core/browser/history_service.h" @@ -906,7 +908,7 @@ bool AutofillDownloadManager::StartRequest(FormRequestData request_data) { url_loader_factory.get(), base::BindOnce(&AutofillDownloadManager::OnSimpleLoaderComplete, base::Unretained(this), std::move(--url_loaders_.end()), - std::move(request_data), base::TimeTicks::Now())); + std::move(request_data), AutofillTickClock::NowTicks())); return true; } @@ -966,9 +968,9 @@ std::string AutofillDownloadManager::GetCombinedSignature( int AutofillDownloadManager::GetMaxServerAttempts() { // This value is constant for the life of the browser, so we cache it // statically on first use to avoid re-parsing the param on each retry - // opportunity. The range is forced to be within [1, 20]. - static int max_attempts = - std::max(1, std::min(20, kAutofillMaxServerAttempts.Get())); + // opportunity. + static const int max_attempts = + base::ClampToRange(kAutofillMaxServerAttempts.Get(), 1, 20); return max_attempts; } @@ -997,7 +999,7 @@ void AutofillDownloadManager::OnSimpleLoaderComplete( LogHttpResponseData(request_data.request_type, response_code, simple_loader->NetError(), - base::TimeTicks::Now() - request_start); + AutofillTickClock::NowTicks() - request_start); // Handle error if there is and return. if (!success) { diff --git a/chromium/components/autofill/core/browser/autofill_download_manager_unittest.cc b/chromium/components/autofill/core/browser/autofill_download_manager_unittest.cc index d3fbc3484e4..a62afad14f7 100644 --- a/chromium/components/autofill/core/browser/autofill_download_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_download_manager_unittest.cc @@ -34,6 +34,7 @@ #include "components/autofill/core/browser/randomized_encoder.h" #include "components/autofill/core/browser/test_autofill_clock.h" #include "components/autofill/core/browser/test_autofill_driver.h" +#include "components/autofill/core/common/autofill_clock.h" #include "components/autofill/core/common/autofill_features.h" #include "components/autofill/core/common/autofill_switches.h" #include "components/autofill/core/common/form_data.h" @@ -229,8 +230,9 @@ class AutofillDownloadManagerTest : public AutofillDownloadManager::Observer, response.signature = form_signature; response.error = http_error; response.type_of_response = - request_type == AutofillDownloadManager::REQUEST_QUERY ? - REQUEST_QUERY_FAILED : REQUEST_UPLOAD_FAILED; + request_type == AutofillDownloadManager::REQUEST_QUERY + ? REQUEST_QUERY_FAILED + : REQUEST_UPLOAD_FAILED; responses_.push_back(response); } @@ -393,7 +395,8 @@ TEST_F(AutofillDownloadManagerTest, QueryAndUploadTest) { "<field autofilltype=\"31\" />" "<field autofilltype=\"33\" />" "</autofillqueryresponse>", - "", "<html></html>", + "", + "<html></html>", }; // Return them out of sequence. @@ -408,7 +411,7 @@ TEST_F(AutofillDownloadManagerTest, QueryAndUploadTest) { // Request 2: Unsuccessful upload. request = test_url_loader_factory_.GetPendingRequest(2); test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList( - request, network::CreateResourceResponseHead(net::HTTP_NOT_FOUND), + request, network::CreateURLResponseHead(net::HTTP_NOT_FOUND), responses[2], network::URLLoaderCompletionStatus(net::OK)); histogram.ExpectBucketCount("Autofill.Upload.HttpResponseOrErrorCode", net::HTTP_NOT_FOUND, 1); @@ -464,8 +467,7 @@ TEST_F(AutofillDownloadManagerTest, QueryAndUploadTest) { AutofillMetrics::QUERY_SENT, 2); histogram.ExpectUniqueSample("Autofill.Query.Method", METHOD_GET, 2); test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList( - request, - network::CreateResourceResponseHead(net::HTTP_INTERNAL_SERVER_ERROR), + request, network::CreateURLResponseHead(net::HTTP_INTERNAL_SERVER_ERROR), responses[0], network::URLLoaderCompletionStatus(net::OK)); histogram.ExpectBucketCount("Autofill.Query.HttpResponseOrErrorCode", net::HTTP_INTERNAL_SERVER_ERROR, 1); @@ -489,7 +491,7 @@ TEST_F(AutofillDownloadManagerTest, QueryAndUploadTest) { network::URLLoaderCompletionStatus status(net::OK); status.exists_in_cache = true; test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList( - request, network::CreateResourceResponseHead(net::HTTP_OK), responses[0], + request, network::CreateURLResponseHead(net::HTTP_OK), responses[0], status); // Check Request 5. @@ -845,9 +847,8 @@ TEST_F(AutofillDownloadManagerTest, BackoffLogic_Query) { // Request error incurs a retry after 1 second. test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList( - request, - network::CreateResourceResponseHead(net::HTTP_INTERNAL_SERVER_ERROR), "", - network::URLLoaderCompletionStatus(net::OK)); + request, network::CreateURLResponseHead(net::HTTP_INTERNAL_SERVER_ERROR), + "", network::URLLoaderCompletionStatus(net::OK)); EXPECT_EQ(1U, responses_.size()); EXPECT_LT(download_manager_.loader_backoff_.GetTimeUntilRelease(), @@ -864,7 +865,7 @@ TEST_F(AutofillDownloadManagerTest, BackoffLogic_Query) { // Next error incurs a retry after 2 seconds. test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList( request, - network::CreateResourceResponseHead(net::HTTP_REQUEST_ENTITY_TOO_LARGE), + network::CreateURLResponseHead(net::HTTP_REQUEST_ENTITY_TOO_LARGE), "<html></html>", network::URLLoaderCompletionStatus(net::OK)); EXPECT_EQ(2U, responses_.size()); @@ -915,9 +916,8 @@ TEST_F(AutofillDownloadManagerTest, BackoffLogic_Upload) { // Error incurs a retry after 1 second. test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList( - request, - network::CreateResourceResponseHead(net::HTTP_INTERNAL_SERVER_ERROR), "", - network::URLLoaderCompletionStatus(net::OK)); + request, network::CreateURLResponseHead(net::HTTP_INTERNAL_SERVER_ERROR), + "", network::URLLoaderCompletionStatus(net::OK)); EXPECT_EQ(1U, responses_.size()); EXPECT_LT(download_manager_.loader_backoff_.GetTimeUntilRelease(), base::TimeDelta::FromMilliseconds(1100)); @@ -959,8 +959,8 @@ TEST_F(AutofillDownloadManagerTest, BackoffLogic_Upload) { request = test_url_loader_factory_.GetPendingRequest(2); test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList( request, - network::CreateResourceResponseHead(net::HTTP_REQUEST_ENTITY_TOO_LARGE), - "", network::URLLoaderCompletionStatus(net::OK)); + network::CreateURLResponseHead(net::HTTP_REQUEST_ENTITY_TOO_LARGE), "", + network::URLLoaderCompletionStatus(net::OK)); ASSERT_EQ(test_url_loader_factory_.NumPending(), 0); histogram.ExpectBucketCount("Autofill.Upload.HttpResponseOrErrorCode", net::HTTP_REQUEST_ENTITY_TOO_LARGE, 1); @@ -1012,7 +1012,7 @@ TEST_F(AutofillDownloadManagerTest, RetryLimit_Query) { // Request error incurs a retry after 1 second. test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList( request, - network::CreateResourceResponseHead(net::HTTP_INTERNAL_SERVER_ERROR), + network::CreateURLResponseHead(net::HTTP_INTERNAL_SERVER_ERROR), "<html></html>", network::URLLoaderCompletionStatus(net::OK)); EXPECT_EQ(1U, responses_.size()); @@ -1089,8 +1089,8 @@ TEST_F(AutofillDownloadManagerTest, RetryLimit_Upload) { // Simulate a server failure. test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList( request, - network::CreateResourceResponseHead(net::HTTP_INTERNAL_SERVER_ERROR), - "", network::URLLoaderCompletionStatus(net::OK)); + network::CreateURLResponseHead(net::HTTP_INTERNAL_SERVER_ERROR), "", + network::URLLoaderCompletionStatus(net::OK)); // Check that it was a failure. ASSERT_EQ(1U, responses_.size()); @@ -1206,25 +1206,25 @@ TEST_F(AutofillDownloadManagerTest, CacheQueryTest) { // Limit cache to two forms. LimitCache(2); - const char *responses[] = { - "<autofillqueryresponse>" + const char* responses[] = { + "<autofillqueryresponse>" "<field autofilltype=\"0\" />" "<field autofilltype=\"3\" />" "<field autofilltype=\"5\" />" - "</autofillqueryresponse>", - "<autofillqueryresponse>" + "</autofillqueryresponse>", + "<autofillqueryresponse>" "<field autofilltype=\"0\" />" "<field autofilltype=\"3\" />" "<field autofilltype=\"5\" />" "<field autofilltype=\"9\" />" - "</autofillqueryresponse>", - "<autofillqueryresponse>" + "</autofillqueryresponse>", + "<autofillqueryresponse>" "<field autofilltype=\"0\" />" "<field autofilltype=\"3\" />" "<field autofilltype=\"5\" />" "<field autofilltype=\"9\" />" "<field autofilltype=\"0\" />" - "</autofillqueryresponse>", + "</autofillqueryresponse>", }; base::HistogramTester histogram; @@ -2179,7 +2179,7 @@ TEST_P(AutofillUploadTest, PeriodicReset) { base::HistogramTester histogram_tester; TestAutofillClock test_clock; - test_clock.SetNow(base::Time::Now()); + test_clock.SetNow(AutofillClock::Now()); // The first attempt should succeed. EXPECT_TRUE(SendUploadRequest(form_structure, true, {}, "", true)); @@ -2237,7 +2237,7 @@ TEST_P(AutofillUploadTest, ResetOnClearUploadHisotry) { base::HistogramTester histogram_tester; TestAutofillClock test_clock; - test_clock.SetNow(base::Time::Now()); + test_clock.SetNow(AutofillClock::Now()); // The first attempt should succeed. EXPECT_TRUE(SendUploadRequest(form_structure, true, {}, "", true)); diff --git a/chromium/components/autofill/core/browser/autofill_driver.h b/chromium/components/autofill/core/browser/autofill_driver.h index b9e205706e4..e8998205270 100644 --- a/chromium/components/autofill/core/browser/autofill_driver.h +++ b/chromium/components/autofill/core/browser/autofill_driver.h @@ -107,9 +107,9 @@ class AutofillDriver { const base::string16& value) = 0; // Tells the renderer to set the currently focused node's corresponding - // accessibility node to |autofill_suggestions_available|. + // accessibility node's autofill state to |state|. virtual void RendererShouldSetSuggestionAvailability( - bool autofill_suggestions_available) = 0; + const mojom::AutofillState state) = 0; // Informs the renderer that the popup has been hidden. virtual void PopupHidden() = 0; diff --git a/chromium/components/autofill/core/browser/autofill_driver_factory_unittest.cc b/chromium/components/autofill/core/browser/autofill_driver_factory_unittest.cc index 54c431018dd..48ccf45049e 100644 --- a/chromium/components/autofill/core/browser/autofill_driver_factory_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_driver_factory_unittest.cc @@ -100,7 +100,7 @@ class AutofillDriverFactoryTest : public testing::Test { protected: // For TestAutofillDriver. - base::test::TaskEnvironment task_environment_; + base::test::SingleThreadTaskEnvironment task_environment_; MockAutofillClient client_; diff --git a/chromium/components/autofill/core/browser/autofill_experiments.cc b/chromium/components/autofill/core/browser/autofill_experiments.cc index 4ad323a4874..dd2bd3874d8 100644 --- a/chromium/components/autofill/core/browser/autofill_experiments.cc +++ b/chromium/components/autofill/core/browser/autofill_experiments.cc @@ -12,7 +12,6 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" -#include "components/autofill/core/browser/autofill_internals_service.h" #include "components/autofill/core/browser/autofill_metrics.h" #include "components/autofill/core/browser/logging/log_manager.h" #include "components/autofill/core/browser/payments/payments_util.h" diff --git a/chromium/components/autofill/core/browser/autofill_external_delegate.cc b/chromium/components/autofill/core/browser/autofill_external_delegate.cc index 60c4d7feb19..a5507ab6194 100644 --- a/chromium/components/autofill/core/browser/autofill_external_delegate.cc +++ b/chromium/components/autofill/core/browser/autofill_external_delegate.cc @@ -153,10 +153,10 @@ bool AutofillExternalDelegate::HasActiveScreenReader() const { } void AutofillExternalDelegate::OnAutofillAvailabilityEvent( - bool has_suggestions) { + const mojom::AutofillState state) { // Availability of suggestions should be communicated to Blink because // accessibility objects live in both the renderer and browser processes. - driver_->RendererShouldSetSuggestionAvailability(has_suggestions); + driver_->RendererShouldSetSuggestionAvailability(state); } void AutofillExternalDelegate::SetCurrentDataListValues( @@ -373,18 +373,6 @@ void AutofillExternalDelegate::ApplyAutofillOptions( #endif } -// On iOS, GooglePayIcon comes at the begining and hence prepended to the list. -#if defined(OS_IOS) - if (base::FeatureList::IsEnabled( - features::kAutofillDownstreamUseGooglePayBrandingOniOS) && - is_all_server_suggestions) { - Suggestion googlepay_icon; - googlepay_icon.icon = "googlePay"; - googlepay_icon.frontend_id = POPUP_ITEM_ID_GOOGLE_PAY_BRANDING; - suggestions->insert(suggestions->begin(), googlepay_icon); - } -#endif - #if defined(OS_ANDROID) if (IsKeyboardAccessoryEnabled()) { suggestions->back().icon = "settings"; diff --git a/chromium/components/autofill/core/browser/autofill_external_delegate.h b/chromium/components/autofill/core/browser/autofill_external_delegate.h index 2e2b8139e78..7cde61e18ac 100644 --- a/chromium/components/autofill/core/browser/autofill_external_delegate.h +++ b/chromium/components/autofill/core/browser/autofill_external_delegate.h @@ -86,9 +86,9 @@ class AutofillExternalDelegate : public AutofillPopupDelegate { // Returns true if there is a screen reader installed on the machine. virtual bool HasActiveScreenReader() const; - // Indicates on focus changed if autofill is available or unavailable, so - // state can be announced by screen readers. - virtual void OnAutofillAvailabilityEvent(bool has_suggestions); + // Indicates on focus changed if autofill/autocomplete is available or + // unavailable, so state can be announced by screen readers. + virtual void OnAutofillAvailabilityEvent(const mojom::AutofillState state); // Set the data list value associated with the current field. void SetCurrentDataListValues( diff --git a/chromium/components/autofill/core/browser/autofill_external_delegate_unittest.cc b/chromium/components/autofill/core/browser/autofill_external_delegate_unittest.cc index a2c018bfd72..89dd815bd17 100644 --- a/chromium/components/autofill/core/browser/autofill_external_delegate_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_external_delegate_unittest.cc @@ -36,7 +36,6 @@ #include "ui/base/l10n/l10n_util.h" #include "ui/gfx/geometry/rect.h" -using autofill::features::kAutofillDownstreamUseGooglePayBrandingOniOS; using base::ASCIIToUTF16; using testing::_; @@ -181,7 +180,7 @@ class AutofillExternalDelegateUnitTest : public testing::Test { kQueryId, suggestions, /*autoselect_first_suggestion=*/false); } - base::test::TaskEnvironment task_environment_; + base::test::SingleThreadTaskEnvironment task_environment_; testing::NiceMock<MockAutofillClient> autofill_client_; std::unique_ptr<testing::NiceMock<MockAutofillDriver>> autofill_driver_; @@ -769,54 +768,6 @@ TEST_F(AutofillExternalDelegateUnitTest, ShouldShowGooglePayIcon) { kQueryId, autofill_item, /*autoselect_first_suggestion=*/false, true); } -#if defined(OS_IOS) -TEST_F(AutofillExternalDelegateUnitTest, ShouldShowGooglePayIconOniOS) { - // Turn on feature flag. - base::test::ScopedFeatureList scoped_feature_list_; - scoped_feature_list_.InitAndEnableFeature( - kAutofillDownstreamUseGooglePayBrandingOniOS); - IssueOnQuery(kQueryId); - - auto element_icons = - testing::ElementsAre("googlePay", std::string(), "googlePay"); - EXPECT_CALL(autofill_client_, - ShowAutofillPopup(_, _, SuggestionVectorIconsAre(element_icons), - false, PopupType::kPersonalInformation, _)); - - std::vector<Suggestion> autofill_item; - autofill_item.push_back(Suggestion()); - autofill_item[0].frontend_id = kAutofillProfileId; - - // This should call ShowAutofillPopup. - external_delegate_->OnSuggestionsReturned( - kQueryId, autofill_item, /*autoselect_first_suggestion=*/false, true); -} - -TEST_F(AutofillExternalDelegateUnitTest, - ShouldNotShowGooglePayIconOniOSIfExperimentOff) { - // Turn on feature flag. - base::test::ScopedFeatureList scoped_feature_list_; - scoped_feature_list_.InitAndDisableFeature( - kAutofillDownstreamUseGooglePayBrandingOniOS); - IssueOnQuery(kQueryId); - - auto element_icons = testing::ElementsAre( - std::string(), - std::string() /* Autofill setting item does not have icon. */); - EXPECT_CALL(autofill_client_, - ShowAutofillPopup(_, _, SuggestionVectorIconsAre(element_icons), - false, PopupType::kPersonalInformation, _)); - - std::vector<Suggestion> autofill_item; - autofill_item.push_back(Suggestion()); - autofill_item[0].frontend_id = kAutofillProfileId; - - // This should call ShowAutofillPopup. - external_delegate_->OnSuggestionsReturned( - kQueryId, autofill_item, /*autoselect_first_suggestion=*/false, false); -} -#endif // defined(OS_IOS) - TEST_F(AutofillExternalDelegateUnitTest, ShouldNotShowGooglePayIconIfSuggestionsContainLocalCards) { IssueOnQuery(kQueryId); diff --git a/chromium/components/autofill/core/browser/autofill_field.cc b/chromium/components/autofill/core/browser/autofill_field.cc index 7068d125081..1381fb7c800 100644 --- a/chromium/components/autofill/core/browser/autofill_field.cc +++ b/chromium/components/autofill/core/browser/autofill_field.cc @@ -14,39 +14,28 @@ namespace autofill { -AutofillField::AutofillField() - : server_type_(NO_SERVER_DATA), - heuristic_type_(UNKNOWN_TYPE), - overall_type_(AutofillType(NO_SERVER_DATA)), - html_type_(HTML_TYPE_UNSPECIFIED), - html_mode_(HTML_MODE_NONE), - phone_part_(IGNORED), - credit_card_number_offset_(0), - previously_autofilled_(false), - only_fill_when_focused_(false), - generation_type_(AutofillUploadContents::Field::NO_GENERATION), - generated_password_changed_(false), - vote_type_(AutofillUploadContents::Field::NO_INFORMATION) {} +AutofillField::AutofillField() = default; + +AutofillField::AutofillField(FieldSignature field_signature) + : field_signature_(field_signature) {} AutofillField::AutofillField(const FormFieldData& field, const base::string16& unique_name) : FormFieldData(field), unique_name_(unique_name), - server_type_(NO_SERVER_DATA), - heuristic_type_(UNKNOWN_TYPE), - overall_type_(AutofillType(NO_SERVER_DATA)), - html_type_(HTML_TYPE_UNSPECIFIED), - html_mode_(HTML_MODE_NONE), - phone_part_(IGNORED), - credit_card_number_offset_(0), - previously_autofilled_(false), - only_fill_when_focused_(false), - parseable_name_(field.name), - generation_type_(AutofillUploadContents::Field::NO_GENERATION), - generated_password_changed_(false), - vote_type_(AutofillUploadContents::Field::NO_INFORMATION) {} - -AutofillField::~AutofillField() {} + parseable_name_(field.name) { + field_signature_ = + CalculateFieldSignatureByNameAndType(name, form_control_type); +} + +AutofillField::~AutofillField() = default; + +std::unique_ptr<AutofillField> AutofillField::CreateForPasswordManagerUpload( + FieldSignature field_signature) { + std::unique_ptr<AutofillField> field; + field.reset(new AutofillField(field_signature)); + return field; +} void AutofillField::set_heuristic_type(ServerFieldType type) { if (type >= 0 && type < MAX_VALID_FIELD_TYPE && @@ -178,7 +167,9 @@ bool AutofillField::IsEmpty() const { } FieldSignature AutofillField::GetFieldSignature() const { - return CalculateFieldSignatureByNameAndType(name, form_control_type); + return field_signature_ + ? *field_signature_ + : CalculateFieldSignatureByNameAndType(name, form_control_type); } std::string AutofillField::FieldSignatureAsStr() const { diff --git a/chromium/components/autofill/core/browser/autofill_field.h b/chromium/components/autofill/core/browser/autofill_field.h index e46ff91865a..30856f92d7b 100644 --- a/chromium/components/autofill/core/browser/autofill_field.h +++ b/chromium/components/autofill/core/browser/autofill_field.h @@ -43,6 +43,12 @@ class AutofillField : public FormFieldData { AutofillField(const FormFieldData& field, const base::string16& unique_name); virtual ~AutofillField(); + // Creates AutofillField that has bare minimum information for uploading + // votes, namely a field signature. Warning: do not use for Autofill code, + // since it is likely missing some fields. + static std::unique_ptr<AutofillField> CreateForPasswordManagerUpload( + FieldSignature field_signature); + const base::string16& unique_name() const { return unique_name_; } ServerFieldType heuristic_type() const { return heuristic_type_; } @@ -129,9 +135,6 @@ class AutofillField : public FormFieldData { // field). bool IsFieldFillable() const; - void set_default_value(const std::string& value) { default_value_ = value; } - const std::string& default_value() const { return default_value_; } - void set_initial_value_hash(uint32_t value) { initial_value_hash_ = value; } base::Optional<uint32_t> initial_value_hash() { return initial_value_hash_; } @@ -181,14 +184,17 @@ class AutofillField : public FormFieldData { void NormalizePossibleTypesValidities(); private: + explicit AutofillField(FieldSignature field_signature); + // Whether the heuristics or server predict a credit card field. bool IsCreditCardPrediction() const; + base::Optional<FieldSignature> field_signature_; // The unique name of this field, generated by Autofill. base::string16 unique_name_; // The type of the field, as determined by the Autofill server. - ServerFieldType server_type_; + ServerFieldType server_type_ = NO_SERVER_DATA; // The possible types of the field, as determined by the Autofill server, // including |server_type_| as the first item. @@ -200,7 +206,7 @@ class AutofillField : public FormFieldData { base::Optional<PasswordRequirementsSpec> password_requirements_; // The type of the field, as determined by the local heuristics. - ServerFieldType heuristic_type_; + ServerFieldType heuristic_type_ = UNKNOWN_TYPE; // The type of the field. Overrides all other types (html_type_, // heuristic_type_, server_type_). @@ -209,11 +215,11 @@ class AutofillField : public FormFieldData { AutofillType overall_type_; // The type of the field, as specified by the site author in HTML. - HtmlFieldType html_type_; + HtmlFieldType html_type_ = HTML_TYPE_UNSPECIFIED; // The "mode" of the field, as specified by the site author in HTML. // Currently this is used to distinguish between billing and shipping fields. - HtmlFieldMode html_mode_; + HtmlFieldMode html_mode_ = HTML_MODE_NONE; // The set of possible types for this field. ServerFieldTypeSet possible_types_; @@ -222,10 +228,7 @@ class AutofillField : public FormFieldData { ServerFieldTypeValidityStatesMap possible_types_validities_; // Used to track whether this field is a phone prefix or suffix. - PhonePart phone_part_; - - // The default value returned by the Autofill server. - std::string default_value_; + PhonePart phone_part_ = IGNORED; // A low-entropy hash of the field's initial value before user-interactions or // automatic fillings. This field is used to detect static placeholders. @@ -233,13 +236,13 @@ class AutofillField : public FormFieldData { // Used to hold the position of the first digit to be copied as a substring // from credit card number. - size_t credit_card_number_offset_; + size_t credit_card_number_offset_ = 0; // Whether the field was autofilled then later edited. - bool previously_autofilled_; + bool previously_autofilled_ = false; // Whether the field should be filled when it is not the highlighted field. - bool only_fill_when_focused_; + bool only_fill_when_focused_ = false; // The parseable name attribute, with unnecessary information removed (such as // a common prefix shared with other fields). Will be used for heuristics @@ -247,15 +250,17 @@ class AutofillField : public FormFieldData { base::string16 parseable_name_; // The type of password generation event, if it happened. - AutofillUploadContents::Field::PasswordGenerationType generation_type_; + AutofillUploadContents::Field::PasswordGenerationType generation_type_ = + AutofillUploadContents::Field::NO_GENERATION; // Whether the generated password was changed by user. - bool generated_password_changed_; + bool generated_password_changed_ = false; // The vote type, if the autofill type is USERNAME or any password vote. // Otherwise, the field is ignored. |vote_type_| provides context as to what // triggered the vote. - AutofillUploadContents::Field::VoteType vote_type_; + AutofillUploadContents::Field::VoteType vote_type_ = + AutofillUploadContents::Field::NO_INFORMATION; DISALLOW_COPY_AND_ASSIGN(AutofillField); }; diff --git a/chromium/components/autofill/core/browser/autofill_handler.cc b/chromium/components/autofill/core/browser/autofill_handler.cc index 065949cfb27..057fda666c7 100644 --- a/chromium/components/autofill/core/browser/autofill_handler.cc +++ b/chromium/components/autofill/core/browser/autofill_handler.cc @@ -8,7 +8,10 @@ #include "components/autofill/core/browser/form_structure.h" #include "components/autofill/core/browser/logging/log_manager.h" #include "components/autofill/core/common/autofill_data_validation.h" +#include "components/autofill/core/common/autofill_internals/log_message.h" +#include "components/autofill/core/common/autofill_internals/logging_scope.h" #include "components/autofill/core/common/autofill_payments_features.h" +#include "components/autofill/core/common/autofill_tick_clock.h" #include "components/autofill/core/common/signatures_util.h" #include "ui/gfx/geometry/rect_f.h" @@ -82,34 +85,27 @@ void AutofillHandler::OnFormsSeen(const std::vector<FormData>& forms, // the pointer values. std::set<FormSignature> new_form_signatures; for (const FormData& form : forms) { - const auto parse_form_start_time = TimeTicks::Now(); + const auto parse_form_start_time = AutofillTickClock::NowTicks(); FormStructure* cached_form_structure = nullptr; FormStructure* form_structure = nullptr; // Try to find the FormStructure that corresponds to |form| if the form // contains credit card fields only. // |cached_form_structure| may still be nullptr after this call. - if (base::FeatureList::IsEnabled(features::kAutofillImportDynamicForms)) { - ignore_result(FindCachedForm(form, &cached_form_structure)); - bool only_contains_credit_card_fields = true; - if (cached_form_structure) { - for (const FormType& form_type : - cached_form_structure->GetFormTypes()) { - if (form_type != CREDIT_CARD_FORM) { - only_contains_credit_card_fields = false; - break; - } + ignore_result(FindCachedForm(form, &cached_form_structure)); + if (cached_form_structure) { + for (const FormType& form_type : cached_form_structure->GetFormTypes()) { + if (form_type != CREDIT_CARD_FORM) { + cached_form_structure = nullptr; + break; } } - if (!only_contains_credit_card_fields) { - cached_form_structure = nullptr; - } } if (!ParseForm(form, cached_form_structure, &form_structure)) continue; DCHECK(form_structure); new_form_signatures.insert(form_structure->form_signature()); - AutofillMetrics::LogParseFormTiming(TimeTicks::Now() - + AutofillMetrics::LogParseFormTiming(AutofillTickClock::NowTicks() - parse_form_start_time); } @@ -274,12 +270,17 @@ bool AutofillHandler::ParseForm(const FormData& form, const FormStructure* cached_form, FormStructure** parsed_form_structure) { DCHECK(parsed_form_structure); - if (form_structures_.size() >= kAutofillHandlerMaxFormCacheSize) + if (form_structures_.size() >= kAutofillHandlerMaxFormCacheSize) { + if (log_manager_) { + log_manager_->Log() << LoggingScope::kAbortParsing + << LogMessage::kAbortParsingTooManyForms << form; + } return false; + } auto form_structure = std::make_unique<FormStructure>(form); form_structure->ParseFieldTypesFromAutocompleteAttributes(); - if (!form_structure->ShouldBeParsed()) + if (!form_structure->ShouldBeParsed(log_manager_)) return false; if (cached_form) { @@ -291,9 +292,8 @@ bool AutofillHandler::ParseForm(const FormData& form, if (observer_for_testing_) observer_for_testing_->OnFormParsed(); - if (form_structure.get()->value_from_dynamic_change_form()) { + if (form_structure.get()->value_from_dynamic_change_form()) value_from_dynamic_change_form_ = true; - } } form_structure->DetermineHeuristicTypes(log_manager_); diff --git a/chromium/components/autofill/core/browser/autofill_internals_service.cc b/chromium/components/autofill/core/browser/autofill_internals_service.cc deleted file mode 100644 index 3f21b7db1d7..00000000000 --- a/chromium/components/autofill/core/browser/autofill_internals_service.cc +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2019 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 "components/autofill/core/browser/autofill_internals_service.h" - -namespace autofill { - -LogBuffer& operator<<(LogBuffer& buf, LoggingScope scope) { - if (!buf.active()) - return buf; - return buf << Tag{"div"} << Attrib{"scope", LoggingScopeToString(scope)} - << Attrib{"class", "log-entry"}; -} - -LogBuffer& operator<<(LogBuffer& buf, LogMessage message) { - if (!buf.active()) - return buf; - return buf << Tag{"div"} << Attrib{"message", LogMessageToString(message)} - << Attrib{"class", "log-message"} << LogMessageValue(message); -} - -} // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_internals_service.h b/chromium/components/autofill/core/browser/autofill_internals_service.h deleted file mode 100644 index b5c81130d26..00000000000 --- a/chromium/components/autofill/core/browser/autofill_internals_service.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2019 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. - -#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_INTERNALS_SERVICE_H_ -#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_INTERNALS_SERVICE_H_ - -#include "base/macros.h" -#include "components/autofill/core/browser/logging/log_buffer.h" -#include "components/autofill/core/browser/logging/log_router.h" -#include "components/autofill/core/common/autofill_internals/log_message.h" -#include "components/autofill/core/common/autofill_internals/logging_scope.h" - -namespace autofill { - -// TODO(crbug.com/928595) This is a temporary home for these operations. -// Find a properly named file. -LogBuffer& operator<<(LogBuffer& buf, LoggingScope scope); - -LogBuffer& operator<<(LogBuffer& buf, LogMessage message); - -} // namespace autofill - -#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_INTERNALS_SERVICE_H_ diff --git a/chromium/components/autofill/core/browser/autofill_manager.cc b/chromium/components/autofill/core/browser/autofill_manager.cc index 98281bbf87f..a23e3c536e6 100644 --- a/chromium/components/autofill/core/browser/autofill_manager.cc +++ b/chromium/components/autofill/core/browser/autofill_manager.cc @@ -42,7 +42,6 @@ #include "components/autofill/core/browser/autofill_data_util.h" #include "components/autofill/core/browser/autofill_external_delegate.h" #include "components/autofill/core/browser/autofill_field.h" -#include "components/autofill/core/browser/autofill_internals_service.h" #include "components/autofill/core/browser/autofill_manager_test_delegate.h" #include "components/autofill/core/browser/autofill_metrics.h" #include "components/autofill/core/browser/autofill_type.h" @@ -68,9 +67,12 @@ #include "components/autofill/core/common/autofill_constants.h" #include "components/autofill/core/common/autofill_data_validation.h" #include "components/autofill/core/common/autofill_features.h" +#include "components/autofill/core/common/autofill_internals/log_message.h" +#include "components/autofill/core/common/autofill_internals/logging_scope.h" #include "components/autofill/core/common/autofill_payments_features.h" #include "components/autofill/core/common/autofill_prefs.h" #include "components/autofill/core/common/autofill_switches.h" +#include "components/autofill/core/common/autofill_tick_clock.h" #include "components/autofill/core/common/autofill_util.h" #include "components/autofill/core/common/form_data.h" #include "components/autofill/core/common/form_data_predictions.h" @@ -345,6 +347,17 @@ AutofillField* GetBestPossibleCVCField( return HeuristicallyFindCVCField(form_structure); } +// Some autofill types are detected based on values and not based on form +// features. We may decide that it's an autofill form after submission. +bool ContainsAutofillableValue(const autofill::FormStructure& form) { + return std::any_of(form.begin(), form.end(), + [](const std::unique_ptr<autofill::AutofillField>& field) { + return base::Contains(field->possible_types(), + UPI_VPA) || + IsUPIVirtualPaymentAddress(field->value); + }); +} + } // namespace AutofillManager::FillingContext::FillingContext() = default; @@ -587,8 +600,10 @@ void AutofillManager::OnFormSubmittedImpl(const FormData& form, enable_ablation_logging_, sync_state_, *submitted_form); } - if (!submitted_form->IsAutofillable()) + if (!submitted_form->IsAutofillable() && + !ContainsAutofillableValue(*submitted_form)) { return; + } // Update Personal Data with the form's submitted data. // Also triggers offering local/upload credit card save, if applicable. @@ -660,8 +675,8 @@ bool AutofillManager::MaybeStartVoteUploadProcess( base::BindOnce(&AutofillManager::UploadFormDataAsyncCallback, weak_ptr_factory_.GetWeakPtr(), base::Owned(form_structure.release()), - initial_interaction_timestamp_, base::TimeTicks::Now(), - observed_submission)); + initial_interaction_timestamp_, + AutofillTickClock::NowTicks(), observed_submission)); return true; } @@ -927,7 +942,7 @@ void AutofillManager::FillOrPreviewProfileForm( auto filling_context = std::make_unique<FillingContext>(); filling_context->temp_data_model = profile; filling_context->filled_field_name = autofill_field->unique_name(); - filling_context->original_fill_time = base::TimeTicks::Now(); + filling_context->original_fill_time = AutofillTickClock::NowTicks(); entry = std::move(filling_context); } } @@ -993,10 +1008,13 @@ void AutofillManager::OnFocusNoLongerOnForm() { #if defined(OS_CHROMEOS) // There is no way of determining whether ChromeVox is in use, so assume it's // being used. - external_delegate_->OnAutofillAvailabilityEvent(false); + external_delegate_->OnAutofillAvailabilityEvent( + mojom::AutofillState::kNoSuggestions); #else - if (external_delegate_->HasActiveScreenReader()) - external_delegate_->OnAutofillAvailabilityEvent(false); + if (external_delegate_->HasActiveScreenReader()) { + external_delegate_->OnAutofillAvailabilityEvent( + mojom::AutofillState::kNoSuggestions); + } #endif } @@ -1018,8 +1036,10 @@ void AutofillManager::OnFocusOnFormFieldImpl(const FormData& form, GetAvailableSuggestions(form, field, &suggestions, &context); external_delegate_->OnAutofillAvailabilityEvent( - context.suppress_reason == SuppressReason::kNotSuppressed && - !suggestions.empty()); + (context.suppress_reason == SuppressReason::kNotSuppressed && + !suggestions.empty()) + ? mojom::AutofillState::kAutofillAvailable + : mojom::AutofillState::kNoSuggestions); } void AutofillManager::OnSelectControlDidChangeImpl( @@ -1355,6 +1375,9 @@ void AutofillManager::OnSuggestionsReturned( int query_id, bool autoselect_first_suggestion, const std::vector<Suggestion>& suggestions) { + external_delegate_->OnAutofillAvailabilityEvent( + !suggestions.empty() ? mojom::AutofillState::kAutocompleteAvailable + : mojom::AutofillState::kNoSuggestions); external_delegate_->OnSuggestionsReturned(query_id, suggestions, autoselect_first_suggestion); } @@ -2199,7 +2222,7 @@ bool AutofillManager::ShouldTriggerRefill(const FormStructure& form_structure) { form_structure); FillingContext* filling_context = itr->second.get(); - base::TimeTicks now = base::TimeTicks::Now(); + base::TimeTicks now = AutofillTickClock::NowTicks(); base::TimeDelta delta = now - filling_context->original_fill_time; if (filling_context->attempted_refill && diff --git a/chromium/components/autofill/core/browser/autofill_manager.h b/chromium/components/autofill/core/browser/autofill_manager.h index 905f5b8ff4a..d0954dea221 100644 --- a/chromium/components/autofill/core/browser/autofill_manager.h +++ b/chromium/components/autofill/core/browser/autofill_manager.h @@ -312,9 +312,6 @@ class AutofillManager : public AutofillHandler, } // Exposed for testing. - AutofillExternalDelegate* external_delegate() { return external_delegate_; } - - // Exposed for testing. void set_download_manager(AutofillDownloadManager* manager) { download_manager_.reset(manager); } @@ -673,6 +670,8 @@ class AutofillManager : public AutofillHandler, CreditCardSelectedFormEvents); FRIEND_TEST_ALL_PREFIXES(AutofillMetricsIFrameTest, CreditCardFilledFormEvents); + FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, + CreditCardUnmaskingPreflightCall); FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, CreditCardGetRealPanDuration); FRIEND_TEST_ALL_PREFIXES(AutofillMetricsIFrameTest, CreditCardWillSubmitFormEvents); diff --git a/chromium/components/autofill/core/browser/autofill_manager_unittest.cc b/chromium/components/autofill/core/browser/autofill_manager_unittest.cc index 3aee9d14555..57355a6eeeb 100644 --- a/chromium/components/autofill/core/browser/autofill_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_manager_unittest.cc @@ -58,6 +58,7 @@ #include "components/autofill/core/common/autofill_payments_features.h" #include "components/autofill/core/common/autofill_prefs.h" #include "components/autofill/core/common/autofill_switches.h" +#include "components/autofill/core/common/autofill_tick_clock.h" #include "components/autofill/core/common/autofill_util.h" #include "components/autofill/core/common/form_data.h" #include "components/autofill/core/common/form_field_data.h" @@ -561,6 +562,13 @@ class AutofillManagerTest : public testing::Test { autofill_manager_->credit_card_access_manager_->cvc_authenticator_ ->full_card_request_.get(); DCHECK(full_card_request); + + // Mock user response. + payments::FullCardRequest::UserProvidedUnmaskDetails details; + details.cvc = base::ASCIIToUTF16("123"); + full_card_request->OnUnmaskPromptAccepted(details); + + // Mock payments response. payments::PaymentsClient::UnmaskResponseDetails response; full_card_request->OnDidGetRealPan(result, response.with_real_pan(real_pan)); @@ -5816,8 +5824,8 @@ TEST_F(AutofillManagerTest, OnTextFieldDidChangeAndUnfocus_Upload) { form.fields[1].value = ASCIIToUTF16("Presley"); form.fields[2].value = ASCIIToUTF16("theking@gmail.com"); // Simulate editing a field. - autofill_manager_->OnTextFieldDidChange(form, form.fields.front(), - gfx::RectF(), base::TimeTicks::Now()); + autofill_manager_->OnTextFieldDidChange( + form, form.fields.front(), gfx::RectF(), AutofillTickClock::NowTicks()); // Simulate lost of focus on the form. autofill_manager_->OnFocusNoLongerOnForm(); @@ -5866,8 +5874,8 @@ TEST_F(AutofillManagerTest, OnTextFieldDidChangeAndNavigation_Upload) { form.fields[1].value = ASCIIToUTF16("Presley"); form.fields[2].value = ASCIIToUTF16("theking@gmail.com"); // Simulate editing a field. - autofill_manager_->OnTextFieldDidChange(form, form.fields.front(), - gfx::RectF(), base::TimeTicks::Now()); + autofill_manager_->OnTextFieldDidChange( + form, form.fields.front(), gfx::RectF(), AutofillTickClock::NowTicks()); // Simulate a navigation so that the pending form is uploaded. autofill_manager_->Reset(); @@ -5915,7 +5923,8 @@ TEST_F(AutofillManagerTest, OnDidFillAutofillFormDataAndUnfocus_Upload) { form.fields[0].value = ASCIIToUTF16("Elvis"); form.fields[1].value = ASCIIToUTF16("Presley"); form.fields[2].value = ASCIIToUTF16("theking@gmail.com"); - autofill_manager_->OnDidFillAutofillFormData(form, base::TimeTicks::Now()); + autofill_manager_->OnDidFillAutofillFormData(form, + AutofillTickClock::NowTicks()); // Simulate lost of focus on the form. autofill_manager_->OnFocusNoLongerOnForm(); @@ -7670,6 +7679,58 @@ TEST_F(AutofillManagerTest, HasSubstr("Autofill.FormEvents.Address")))); } +// Test that we import data when the field type is determined by the value and +// without any heuristics on the attributes. +TEST_F(AutofillManagerTest, ImportDataWhenValueDetected) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature(features::kAutofillSaveAndFillVPA); + + FormData form; + form.url = GURL("https://wwww.foo.com"); + + FormFieldData field; + test::CreateTestFormField("VPA:", "vpa", "", "text", &field); + form.fields.push_back(field); + + FormsSeen({form}); + autofill_manager_->SetExpectedSubmittedFieldTypes({{UPI_VPA}}); + autofill_manager_->SetExpectedObservedSubmission(true); + autofill_manager_->SetCallParentUploadFormData(true); + form.submission_event = + mojom::SubmissionIndicatorEvent::SAME_DOCUMENT_NAVIGATION; + + form.fields[0].value = ASCIIToUTF16("user@indianbank"); + FormSubmitted(form); + + EXPECT_EQ(1, personal_data_.num_times_save_vpa_called()); +} + +// Test that we do not import VPA data when in incognito. +TEST_F(AutofillManagerTest, DontImportVPAWhenIncognito) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature(features::kAutofillSaveAndFillVPA); + autofill_driver_->SetIsIncognito(true); + + FormData form; + form.url = GURL("https://wwww.foo.com"); + + FormFieldData field; + test::CreateTestFormField("VPA:", "vpa", "", "text", &field); + form.fields.push_back(field); + + FormsSeen({form}); + autofill_manager_->SetExpectedSubmittedFieldTypes({{UPI_VPA}}); + autofill_manager_->SetExpectedObservedSubmission(true); + autofill_manager_->SetCallParentUploadFormData(true); + form.submission_event = + mojom::SubmissionIndicatorEvent::SAME_DOCUMENT_NAVIGATION; + + form.fields[0].value = ASCIIToUTF16("user@indianbank"); + FormSubmitted(form); + + EXPECT_EQ(0, personal_data_.num_times_save_vpa_called()); +} + // Test param indicates if there is an active screen reader. class OnFocusOnFormFieldTest : public AutofillManagerTest, public testing::WithParamInterface<bool> { diff --git a/chromium/components/autofill/core/browser/autofill_merge_unittest.cc b/chromium/components/autofill/core/browser/autofill_merge_unittest.cc index a845b3daed5..b3d5aff7e5c 100644 --- a/chromium/components/autofill/core/browser/autofill_merge_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_merge_unittest.cc @@ -20,6 +20,7 @@ #include "components/autofill/core/browser/autofill_test_utils.h" #include "components/autofill/core/browser/autofill_type.h" #include "components/autofill/core/browser/data_driven_test.h" +#include "components/autofill/core/browser/data_model/autofill_profile_comparator.h" #include "components/autofill/core/browser/form_data_importer.h" #include "components/autofill/core/browser/form_structure.h" #include "components/autofill/core/browser/geo/country_names.h" @@ -128,11 +129,9 @@ class PersonalDataManagerMock : public PersonalDataManager { }; PersonalDataManagerMock::PersonalDataManagerMock() - : PersonalDataManager("en-US") { -} + : PersonalDataManager("en-US") {} -PersonalDataManagerMock::~PersonalDataManagerMock() { -} +PersonalDataManagerMock::~PersonalDataManagerMock() {} void PersonalDataManagerMock::Reset() { profiles_.clear(); @@ -141,8 +140,8 @@ void PersonalDataManagerMock::Reset() { std::string PersonalDataManagerMock::SaveImportedProfile( const AutofillProfile& profile) { std::vector<AutofillProfile> profiles; - std::string merged_guid = - MergeProfile(profile, &profiles_, "en-US", &profiles); + std::string merged_guid = AutofillProfileComparator::MergeProfile( + profile, &profiles_, "en-US", &profiles); if (merged_guid == profile.guid()) profiles_.push_back(std::make_unique<AutofillProfile>(profile)); return merged_guid; @@ -202,8 +201,7 @@ AutofillMergeTest::AutofillMergeTest() : DataDrivenTest(GetTestDataDir()) { } } -AutofillMergeTest::~AutofillMergeTest() { -} +AutofillMergeTest::~AutofillMergeTest() {} void AutofillMergeTest::SetUp() { test::DisableSystemServices(nullptr); @@ -247,8 +245,7 @@ void AutofillMergeTest::MergeProfiles(const std::string& profiles, do { ++separator_pos; } while (separator_pos < line.size() && line[separator_pos] == ' '); - base::string16 value = - base::UTF8ToUTF16(line.substr(separator_pos)); + base::string16 value = base::UTF8ToUTF16(line.substr(separator_pos)); base::ReplaceFirstSubstringAfterOffset( &value, 0, base::ASCIIToUTF16("\\n"), base::ASCIIToUTF16("\n")); @@ -279,12 +276,15 @@ void AutofillMergeTest::MergeProfiles(const std::string& profiles, // Import the profile. std::unique_ptr<CreditCard> imported_credit_card; + base::Optional<std::string> unused_imported_vpa; form_data_importer_->ImportFormData(form_structure, true, // address autofill enabled, true, // credit card autofill enabled false, // should return local card - &imported_credit_card); + &imported_credit_card, + &unused_imported_vpa); EXPECT_FALSE(imported_credit_card); + EXPECT_FALSE(unused_imported_vpa.has_value()); // Clear the |form| to start a new profile. form.fields.clear(); diff --git a/chromium/components/autofill/core/browser/autofill_metrics.cc b/chromium/components/autofill/core/browser/autofill_metrics.cc index e2ad2699390..ad4203a2a02 100644 --- a/chromium/components/autofill/core/browser/autofill_metrics.cc +++ b/chromium/components/autofill/core/browser/autofill_metrics.cc @@ -22,6 +22,7 @@ #include "components/autofill/core/browser/form_structure.h" #include "components/autofill/core/common/autofill_clock.h" #include "components/autofill/core/common/autofill_prefs.h" +#include "components/autofill/core/common/autofill_tick_clock.h" #include "components/autofill/core/common/form_data.h" #include "services/metrics/public/cpp/metrics_utils.h" #include "services/metrics/public/cpp/ukm_builders.h" @@ -767,10 +768,17 @@ void AutofillMetrics::LogSaveCardPromptMetricBySecurityLevel( histogram_name += "Local"; } - base::UmaHistogramEnumeration( - security_state::GetSecurityLevelHistogramName( - histogram_name, security_level), - metric, NUM_SAVE_CARD_PROMPT_METRICS); + base::UmaHistogramEnumeration(security_state::GetSecurityLevelHistogramName( + histogram_name, security_level), + metric, NUM_SAVE_CARD_PROMPT_METRICS); +} + +// static +void AutofillMetrics::LogCreditCardUploadFeedbackMetric( + CreditCardUploadFeedbackMetric metric) { + DCHECK_LT(metric, NUM_CREDIT_CARD_UPLOAD_FEEDBACK_METRICS); + UMA_HISTOGRAM_ENUMERATION("Autofill.CreditCardUploadFeedback", metric, + NUM_CREDIT_CARD_UPLOAD_FEEDBACK_METRICS); } // static @@ -922,6 +930,117 @@ void AutofillMetrics::LogSaveCardWithFirstAndLastNameComplete(bool is_local) { } // static +void AutofillMetrics::LogCardUnmaskDurationAfterWebauthn( + const base::TimeDelta& duration, + AutofillClient::PaymentsRpcResult result) { + std::string suffix; + switch (result) { + case AutofillClient::SUCCESS: + suffix = "Success"; + break; + case AutofillClient::TRY_AGAIN_FAILURE: + case AutofillClient::PERMANENT_FAILURE: + suffix = "Failure"; + break; + case AutofillClient::NETWORK_ERROR: + suffix = "NetworkError"; + break; + case AutofillClient::NONE: + NOTREACHED(); + return; + } + base::UmaHistogramLongTimes("Autofill.BetterAuth.CardUnmaskDuration.Fido", + duration); + base::UmaHistogramLongTimes( + "Autofill.BetterAuth.CardUnmaskDuration.Fido." + suffix, duration); +} + +// static +void AutofillMetrics::LogCardUnmaskPreflightCalled() { + UMA_HISTOGRAM_BOOLEAN("Autofill.BetterAuth.CardUnmaskPreflightCalled", true); +} + +// static +void AutofillMetrics::LogCardUnmaskPreflightDuration( + const base::TimeDelta& duration) { + base::UmaHistogramLongTimes("Autofill.BetterAuth.CardUnmaskPreflightDuration", + duration); +} + +// static +void AutofillMetrics::LogWebauthnOptChangeCalled( + bool request_to_opt_in, + bool is_checkout_flow, + WebauthnOptInParameters metric) { + if (!request_to_opt_in) { + DCHECK(!is_checkout_flow); + base::UmaHistogramBoolean( + "Autofill.BetterAuth.OptOutCalled.FromSettingsPage", true); + return; + } + + std::string histogram_name = "Autofill.BetterAuth.OptInCalled."; + histogram_name += is_checkout_flow ? "FromCheckoutFlow" : "FromSettingsPage"; + base::UmaHistogramEnumeration(histogram_name, metric); +} + +// static +void AutofillMetrics::LogWebauthnOptInPromoShown(bool is_checkout_flow) { + std::string suffix = + is_checkout_flow ? "FromCheckoutFlow" : "FromSettingsPage"; + base::UmaHistogramBoolean("Autofill.BetterAuth.OptInPromoShown." + suffix, + true); +} + +// static +void AutofillMetrics::LogWebauthnOptInPromoUserDecision( + bool is_checkout_flow, + WebauthnOptInPromoUserDecisionMetric metric) { + std::string suffix = + (is_checkout_flow ? "FromCheckoutFlow" : "FromSettingsPage"); + base::UmaHistogramEnumeration( + "Autofill.BetterAuth.OptInPromoUserDecision." + suffix, metric); +} + +// static +void AutofillMetrics::LogCardUnmaskTypeDecision( + CardUnmaskTypeDecisionMetric metric) { + base::UmaHistogramEnumeration("Autofill.BetterAuth.CardUnmaskTypeDecision", + metric); +} + +// static +void AutofillMetrics::LogUserPerceivedLatencyOnCardSelection( + PreflightCallEvent event, + bool fido_auth_enabled) { + std::string histogram_name = + "Autofill.BetterAuth.UserPerceivedLatencyOnCardSelection."; + histogram_name += fido_auth_enabled ? "OptedIn" : "OptedOut"; + base::UmaHistogramEnumeration(histogram_name, event); +} + +// static +void AutofillMetrics::LogWebauthnResult(WebauthnFlowEvent event, + WebauthnResultMetric metric) { + std::string histogram_name = "Autofill.BetterAuth.WebauthnResult."; + switch (event) { + case WebauthnFlowEvent::kImmediateAuthentication: + histogram_name += "ImmediateAuthentication"; + break; + case WebauthnFlowEvent::kAuthenticationAfterCvc: + histogram_name += "AuthenticationAfterCVC"; + break; + case WebauthnFlowEvent::kCheckoutOptIn: + histogram_name += "CheckoutOptIn"; + break; + case WebauthnFlowEvent::kSettingsPageOptIn: + histogram_name += "SettingsPageOptIn"; + break; + } + base::UmaHistogramEnumeration(histogram_name, metric); +} + +// static void AutofillMetrics::LogUnmaskPromptEvent(UnmaskPromptEvent event) { UMA_HISTOGRAM_ENUMERATION("Autofill.UnmaskPrompt.Events", event, NUM_UNMASK_PROMPT_EVENTS); @@ -1186,10 +1305,9 @@ void AutofillMetrics::LogUserHappinessBySecurityLevel( return; } - base::UmaHistogramEnumeration( - security_state::GetSecurityLevelHistogramName( - histogram_name, security_level), - metric, NUM_USER_HAPPINESS_METRICS); + base::UmaHistogramEnumeration(security_state::GetSecurityLevelHistogramName( + histogram_name, security_level), + metric, NUM_USER_HAPPINESS_METRICS); } // static @@ -1974,8 +2092,9 @@ int64_t AutofillMetrics::FormInteractionsUkmLogger::MillisecondsSinceFormParsed( const base::TimeTicks& form_parsed_timestamp) const { DCHECK(!form_parsed_timestamp.is_null()); // Use the pinned timestamp as the current time if it's set. - base::TimeTicks now = - pinned_timestamp_.is_null() ? base::TimeTicks::Now() : pinned_timestamp_; + base::TimeTicks now = pinned_timestamp_.is_null() + ? AutofillTickClock::NowTicks() + : pinned_timestamp_; return ukm::GetExponentialBucketMin( (now - form_parsed_timestamp).InMilliseconds(), @@ -1987,7 +2106,7 @@ AutofillMetrics::UkmTimestampPin::UkmTimestampPin( : logger_(logger) { DCHECK(logger_); DCHECK(!logger_->has_pinned_timestamp()); - logger_->set_pinned_timestamp(base::TimeTicks::Now()); + logger_->set_pinned_timestamp(AutofillTickClock::NowTicks()); } AutofillMetrics::UkmTimestampPin::~UkmTimestampPin() { diff --git a/chromium/components/autofill/core/browser/autofill_metrics.h b/chromium/components/autofill/core/browser/autofill_metrics.h index 7ebd53ccb73..278be51a96c 100644 --- a/chromium/components/autofill/core/browser/autofill_metrics.h +++ b/chromium/components/autofill/core/browser/autofill_metrics.h @@ -6,6 +6,7 @@ #define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_METRICS_H_ #include <stddef.h> +#include <memory> #include <set> #include <string> #include <utility> @@ -119,6 +120,8 @@ class AutofillMetrics { // All the required conditions were satisfied even though the form is // dynamic changed. UPLOAD_OFFERED_FROM_DYNAMIC_CHANGE_FORM = 1 << 17, + // The legal message was invalid. + UPLOAD_NOT_OFFERED_INVALID_LEGAL_MESSAGE = 1 << 18, // Update |kNumCardUploadDecisionMetrics| when adding new enum here. }; @@ -299,6 +302,17 @@ class AutofillMetrics { NUM_SAVE_CARD_PROMPT_METRICS, }; + enum CreditCardUploadFeedbackMetric { + // The loading indicator animation which indicates uploading is in progress + // is successfully shown. + CREDIT_CARD_UPLOAD_FEEDBACK_LOADING_ANIMATION_SHOWN, + // The credit card icon with the saving failure badge is shown. + CREDIT_CARD_UPLOAD_FEEDBACK_FAILURE_ICON_SHOWN, + // The failure icon is clicked and the save card failure bubble is shown. + CREDIT_CARD_UPLOAD_FEEDBACK_FAILURE_BUBBLE_SHOWN, + NUM_CREDIT_CARD_UPLOAD_FEEDBACK_METRICS, + }; + // Metrics to measure user interaction with the Manage Cards view // shown when user clicks on the save card icon after accepting // to save a card. @@ -492,7 +506,9 @@ class AutofillMetrics { NOT_OFFERED_SINGLE_LOCAL_CARD = 7, // User used an unsupported local card, we will abort the migration. NOT_OFFERED_USE_UNSUPPORTED_LOCAL_CARD = 8, - kMaxValue = NOT_OFFERED_USE_UNSUPPORTED_LOCAL_CARD, + // Legal message was invalid, we will abort the migration. + NOT_OFFERED_INVALID_LEGAL_MESSAGE = 9, + kMaxValue = NOT_OFFERED_INVALID_LEGAL_MESSAGE, }; // Metrics to track events when local credit card migration is offered. @@ -680,6 +696,79 @@ class AutofillMetrics { NUM_UNMASK_PROMPT_EVENTS, }; + // Events related to user-perceived latency due to GetDetailsForGetRealPan + // call. + enum class PreflightCallEvent { + // Returned before card chosen. + kPreflightCallReturnedBeforeCardChosen = 0, + // Did not return before card was chosen. When opted-in, this means + // the UI had to wait for the call to return. When opted-out, this means we + // did not offer to opt-in. + kCardChosenBeforePreflightCallReturned = 1, + // Preflight call was irrelevant; skipped waiting. + kDidNotChooseMaskedCard = 2, + kMaxValue = kDidNotChooseMaskedCard, + }; + + // Metric for tracking which authentication method was used for a user with + // FIDO authentication enabled. + enum class CardUnmaskTypeDecisionMetric { + // Only WebAuthn prompt was shown. + kFidoOnly = 0, + // CVC authentication was required in addition to WebAuthn. + kCvcThenFido = 1, + kMaxValue = kCvcThenFido, + }; + + // Possible scenarios where a WebAuthn prompt may show. + enum class WebauthnFlowEvent { + // WebAuthn is immediately prompted for unmasking. + kImmediateAuthentication = 0, + // WebAuthn is prompted after a CVC check. + kAuthenticationAfterCvc = 1, + // WebAuthn is prompted after being offered to opt-in from a checkout flow. + kCheckoutOptIn = 2, + // WebAuthn is prompted after being offered to opt-in from the settings + // page. + kSettingsPageOptIn = 3, + kMaxValue = kSettingsPageOptIn, + }; + + // The result of a WebAuthn user-verification prompt. + enum class WebauthnResultMetric { + // User-verification succeeded. + kSuccess = 0, + // Other checks failed (e.g. invalid domain, algorithm unsupported, etc.) + kOtherError = 1, + // User either failed verification or cancelled. + kNotAllowedError = 2, + kMaxValue = kNotAllowedError, + }; + + // The user decision for the WebAuthn opt-in promo. + enum class WebauthnOptInPromoUserDecisionMetric { + // User accepted promo. + kAccepted = 0, + // User immediately declined promo. + kDeclinedImmediately = 1, + // Once user accepts the dialog, a round-trip call to Payments is sent, + // which is required for user authentication. The user has the option to + // cancel the dialog before the round-trip call is returned. + kDeclinedAfterAccepting = 2, + kMaxValue = kDeclinedAfterAccepting, + }; + + // The parameters with which opt change was called. + enum class WebauthnOptInParameters { + // Call made to fetch a challenge. + kFetchingChallenge = 0, + // Call made with signature of creation challenge. + kWithCreationChallenge = 1, + // Call made with signature of request challenge. + kWithRequestChallenge = 2, + kMaxValue = kWithRequestChallenge, + }; + // Possible results of Payments RPCs. enum PaymentsRpcResult { // Request succeeded. @@ -955,6 +1044,8 @@ class AutofillMetrics { SaveCardPromptMetric metric, bool is_uploading, security_state::SecurityLevel security_level); + static void LogCreditCardUploadFeedbackMetric( + CreditCardUploadFeedbackMetric metric); static void LogManageCardsPromptMetric(ManageCardsPromptMetric metric, bool is_uploading); static void LogScanCreditCardPromptMetric(ScanCreditCardPromptMetric metric); @@ -1028,6 +1119,48 @@ class AutofillMetrics { static void LogUserHappinessByProfileFormType(UserHappinessMetric metric, uint32_t profile_form_bitmask); + // Logs the card fetch latency after a WebAuthn prompt. + static void LogCardUnmaskDurationAfterWebauthn( + const base::TimeDelta& duration, + AutofillClient::PaymentsRpcResult result); + + // Logs the count of calls to PaymentsClient::GetUnmaskDetails() (aka + // GetDetailsForGetRealPan). + static void LogCardUnmaskPreflightCalled(); + + // Logs the duration of the PaymentsClient::GetUnmaskDetails() call (aka + // GetDetailsForGetRealPan). + static void LogCardUnmaskPreflightDuration(const base::TimeDelta& duration); + + // Logs the count of calls to PaymentsClient::OptChange() (aka + // UpdateAutofillUserPreference). + static void LogWebauthnOptChangeCalled(bool request_to_opt_in, + bool is_checkout_flow, + WebauthnOptInParameters metric); + + // Logs the number of times the opt-in promo for enabling FIDO authentication + // for card unmasking has been shown. + static void LogWebauthnOptInPromoShown(bool is_checkout_flow); + + // Logs the user response to the opt-in promo for enabling FIDO authentication + // for card unmasking. + static void LogWebauthnOptInPromoUserDecision( + bool is_checkout_flow, + WebauthnOptInPromoUserDecisionMetric metric); + + // Logs which unmask type was used for a user with FIDO authentication + // enabled. + static void LogCardUnmaskTypeDecision(CardUnmaskTypeDecisionMetric metric); + + // Logs the existence of any user-perceived latency between selecting a Google + // Payments server card and seeing a card unmask prompt. + static void LogUserPerceivedLatencyOnCardSelection(PreflightCallEvent event, + bool fido_auth_enabled); + + // Logs the result of a WebAuthn prompt. + static void LogWebauthnResult(WebauthnFlowEvent event, + WebauthnResultMetric metric); + // Logs |event| to the unmask prompt events histogram. static void LogUnmaskPromptEvent(UnmaskPromptEvent event); @@ -1258,13 +1391,6 @@ class AutofillMetrics { // autofilled to support synthetic fields. static void LogHiddenOrPresentationalSelectFieldsFilled(); - // Logs the the |ukm_entry_name| with the specified |url| and the specified - // |metrics|. Returns whether the ukm was sucessfully logged. - static bool LogUkm(ukm::UkmRecorder* ukm_recorder, - const GURL& url, - const std::string& ukm_entry_name, - const std::vector<std::pair<const char*, int>>& metrics); - // Converts form type to bit vector to store in UKM. static int64_t FormTypesToBitVector(const std::set<FormType>& form_types); @@ -1288,7 +1414,7 @@ class AutofillMetrics { private: static void Log(AutocompleteEvent event); - static const int kNumCardUploadDecisionMetrics = 18; + static const int kNumCardUploadDecisionMetrics = 19; DISALLOW_IMPLICIT_CONSTRUCTORS(AutofillMetrics); }; diff --git a/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc b/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc index 33cc102717a..1eac2646442 100644 --- a/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc @@ -21,6 +21,7 @@ #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" #include "base/time/time.h" +#include "build/build_config.h" #include "components/autofill/core/browser/autofill_data_util.h" #include "components/autofill/core/browser/autofill_external_delegate.h" #include "components/autofill/core/browser/autofill_test_utils.h" @@ -28,6 +29,7 @@ #include "components/autofill/core/browser/metrics/credit_card_form_event_logger.h" #include "components/autofill/core/browser/metrics/form_events.h" #include "components/autofill/core/browser/mock_autocomplete_history_manager.h" +#include "components/autofill/core/browser/payments/credit_card_access_manager.h" #include "components/autofill/core/browser/payments/test_credit_card_save_manager.h" #include "components/autofill/core/browser/payments/test_payments_client.h" #include "components/autofill/core/browser/personal_data_manager.h" @@ -35,6 +37,7 @@ #include "components/autofill/core/browser/test_autofill_client.h" #include "components/autofill/core/browser/test_autofill_driver.h" #include "components/autofill/core/browser/test_autofill_manager.h" +#include "components/autofill/core/browser/test_autofill_tick_clock.h" #include "components/autofill/core/browser/test_form_data_importer.h" #include "components/autofill/core/browser/test_form_structure.h" #include "components/autofill/core/browser/test_personal_data_manager.h" @@ -43,6 +46,8 @@ #include "components/autofill/core/browser/webdata/autofill_webdata_service.h" #include "components/autofill/core/common/autofill_clock.h" #include "components/autofill/core/common/autofill_features.h" +#include "components/autofill/core/common/autofill_payments_features.h" +#include "components/autofill/core/common/autofill_tick_clock.h" #include "components/autofill/core/common/form_data.h" #include "components/autofill/core/common/form_field_data.h" #include "components/prefs/pref_service.h" @@ -57,6 +62,10 @@ #include "ui/gfx/geometry/rect.h" #include "url/gurl.h" +#if !defined(OS_IOS) +#include "components/autofill/core/browser/payments/test_credit_card_fido_authenticator.h" +#endif + using base::ASCIIToUTF16; using base::Bucket; using base::TimeTicks; @@ -268,6 +277,10 @@ class AutofillMetricsTest : public testing::Test { bool include_masked_server_credit_card, bool include_full_server_credit_card); + // If set to true, then user is capable of using FIDO authentication for card + // unmasking. + void SetFidoEligibility(bool is_verifiable); + // Mocks a RPC response from payments. void OnDidGetRealPan(AutofillClient::PaymentsRpcResult result, const std::string& real_pan); @@ -343,6 +356,13 @@ void AutofillMetricsTest::SetUp() { autofill_manager_.get(), autofill_driver_.get()); autofill_manager_->SetExternalDelegate(external_delegate_.get()); +#if !defined(OS_IOS) + autofill_manager_->credit_card_access_manager() + ->set_fido_authenticator_for_testing( + std::make_unique<TestCreditCardFIDOAuthenticator>( + autofill_driver_.get(), &autofill_client_)); +#endif + // Initialize the TestPersonalDataManager with some default data. CreateTestAutofillProfiles(); } @@ -392,6 +412,22 @@ void AutofillMetricsTest::RecreateProfile(bool is_server) { personal_data_->Refresh(); } +void AutofillMetricsTest::SetFidoEligibility(bool is_verifiable) { + CreditCardAccessManager* access_manager = + autofill_manager_->credit_card_access_manager(); +#if !defined(OS_IOS) + static_cast<TestCreditCardFIDOAuthenticator*>( + access_manager->GetOrCreateFIDOAuthenticator()) + ->SetUserVerifiable(is_verifiable); +#endif + static_cast<payments::TestPaymentsClient*>( + autofill_client_.GetPaymentsClient()) + ->AllowFidoRegistration(true); + access_manager->is_authentication_in_progress_ = false; + access_manager->can_fetch_unmask_details_.Signal(); + access_manager->is_user_verifiable_ = base::nullopt; +} + void AutofillMetricsTest::OnDidGetRealPan( AutofillClient::PaymentsRpcResult result, const std::string& real_pan) { @@ -400,6 +436,12 @@ void AutofillMetricsTest::OnDidGetRealPan( ->GetOrCreateCVCAuthenticator() ->full_card_request_.get(); DCHECK(full_card_request); + + // Fake user response. + payments::FullCardRequest::UserProvidedUnmaskDetails details; + details.cvc = base::ASCIIToUTF16("123"); + full_card_request->OnUnmaskPromptAccepted(details); + payments::PaymentsClient::UnmaskResponseDetails response; full_card_request->OnDidGetRealPan(result, response.with_real_pan(real_pan)); } @@ -1809,7 +1851,7 @@ TEST_F(AutofillMetricsTest, TimingMetrics) { // Simulate a OnFormsSeen() call that should trigger the recording. std::vector<FormData> forms; forms.push_back(form); - autofill_manager_->OnFormsSeen(forms, TimeTicks::Now()); + autofill_manager_->OnFormsSeen(forms, AutofillTickClock::NowTicks()); // Because these metrics are related to timing, it is not possible to know in // advance which bucket the sample will fall into, so we just need to make @@ -2655,7 +2697,7 @@ TEST_F(AutofillMetricsTest, base::test::ScopedFeatureList features; features.InitAndEnableFeature( kAutofillEnforceMinRequiredFieldsForHeuristics); - autofill_manager_->OnFormsSeen(forms, TimeTicks::Now()); + autofill_manager_->OnFormsSeen(forms, AutofillTickClock::NowTicks()); autofill_manager_->Reset(); EXPECT_EQ(0ul, test_ukm_recorder_->entries_count()); @@ -2668,7 +2710,7 @@ TEST_F(AutofillMetricsTest, // Expect the "form parsed without field type hints" metric and the // "form loaded" form interaction event to be logged. { - autofill_manager_->OnFormsSeen(forms, TimeTicks::Now()); + autofill_manager_->OnFormsSeen(forms, AutofillTickClock::NowTicks()); autofill_manager_->Reset(); VerifyDeveloperEngagementUkm( @@ -2718,7 +2760,7 @@ TEST_F(AutofillMetricsTest, // Expect the "form parsed without field type hints" metric and the // "form loaded" form interaction event to be logged. { - autofill_manager_->OnFormsSeen(forms, TimeTicks::Now()); + autofill_manager_->OnFormsSeen(forms, AutofillTickClock::NowTicks()); autofill_manager_->Reset(); VerifyDeveloperEngagementUkm( @@ -2760,7 +2802,7 @@ TEST_F(AutofillMetricsTest, UkmDeveloperEngagement_LogUpiVpaTypeHint) { // interaction event to be logged. { SCOPED_TRACE("VPA is the only hint"); - autofill_manager_->OnFormsSeen(forms, TimeTicks::Now()); + autofill_manager_->OnFormsSeen(forms, AutofillTickClock::NowTicks()); VerifyDeveloperEngagementUkm( test_ukm_recorder_, forms.back(), /*is_for_credit_card=*/false, @@ -2777,7 +2819,7 @@ TEST_F(AutofillMetricsTest, UkmDeveloperEngagement_LogUpiVpaTypeHint) { { SCOPED_TRACE("VPA and other autocomplete hint present"); - autofill_manager_->OnFormsSeen(forms, TimeTicks::Now()); + autofill_manager_->OnFormsSeen(forms, AutofillTickClock::NowTicks()); VerifyDeveloperEngagementUkm( test_ukm_recorder_, forms.back(), /*is_for_credit_card=*/false, @@ -3254,7 +3296,7 @@ TEST_F(AutofillMetricsTest, UpiVpaUkmTest) { std::vector<FormData> forms(1, form); { - autofill_manager_->OnFormsSeen(forms, TimeTicks::Now()); + autofill_manager_->OnFormsSeen(forms, AutofillTickClock::NowTicks()); VerifySubmitFormUkm(test_ukm_recorder_, forms.back(), AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA, @@ -4252,6 +4294,124 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardFilledFormEvents) { } } +// Test that we log preflight calls for credit card unmasking. +TEST_F(AutofillMetricsTest, CreditCardUnmaskingPreflightCall) { + scoped_feature_list_.InitAndEnableFeature( + features::kAutofillCreditCardAuthentication); + std::string preflight_call_metric = + "Autofill.BetterAuth.CardUnmaskPreflightCalled"; + std::string preflight_latency_metric = + "Autofill.BetterAuth.CardUnmaskPreflightDuration"; + + // Set up our form data. + FormData form; + FormFieldData field; + std::vector<ServerFieldType> field_types; + test::CreateTestFormField("Credit card", "card", "", "text", &field); + form.fields.push_back(field); + field_types.push_back(CREDIT_CARD_NUMBER); + + // Simulate having seen this form on page load. + // |form_structure| will be owned by |autofill_manager_|. + autofill_manager_->AddSeenForm(form, field_types, field_types); + + { + // Create local cards and set user as eligible for FIDO authentication. + base::HistogramTester histogram_tester; + RecreateCreditCards(true /* include_local_credit_card */, + false /* include_masked_server_credit_card */, + false /* include_full_server_credit_card */); + SetFidoEligibility(true); + autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, + form.fields[0]); + // If no masked server cards are available, then no preflight call is made. + histogram_tester.ExpectTotalCount(preflight_call_metric, 0); + histogram_tester.ExpectTotalCount(preflight_latency_metric, 0); + } + + { + // Create masked server cards and set user as ineligible for FIDO + // authentication. + base::HistogramTester histogram_tester; + RecreateCreditCards(false /* include_local_credit_card */, + true /* include_masked_server_credit_card */, + false /* include_full_server_credit_card */); + SetFidoEligibility(false); + autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, + form.fields[0]); + // If user is not verifiable, then no preflight call is made. + histogram_tester.ExpectTotalCount(preflight_call_metric, 0); + histogram_tester.ExpectTotalCount(preflight_latency_metric, 0); + } + + { + // Create full server cards and set user as eligible for FIDO + // authentication. + base::HistogramTester histogram_tester; + RecreateCreditCards(false /* include_local_credit_card */, + false /* include_masked_server_credit_card */, + true /* include_full_server_credit_card */); + SetFidoEligibility(false); + autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, + form.fields[0]); + // If no masked server cards are available, then no preflight call is made. + histogram_tester.ExpectTotalCount(preflight_call_metric, 0); + histogram_tester.ExpectTotalCount(preflight_latency_metric, 0); + } + + { + // Create masked server cards and set user as eligible for FIDO + // authentication. + base::HistogramTester histogram_tester; + RecreateCreditCards(false /* include_local_credit_card */, + true /* include_masked_server_credit_card */, + false /* include_full_server_credit_card */); + SetFidoEligibility(true); + autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, + form.fields[0]); + std::string guid( + "10000000-0000-0000-0000-000000000002"); // masked server card + autofill_manager_->FillOrPreviewForm( + AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.back(), + autofill_manager_->MakeFrontendID(guid, std::string())); + // Preflight call is made only if a masked server card is available and the + // user is eligible for FIDO authentication (except iOS). +#if defined(OS_IOS) + histogram_tester.ExpectTotalCount(preflight_call_metric, 0); + histogram_tester.ExpectTotalCount(preflight_latency_metric, 0); +#else + histogram_tester.ExpectTotalCount(preflight_call_metric, 1); + histogram_tester.ExpectTotalCount(preflight_latency_metric, 1); +#endif + } + + { + // Create all types of cards and set user as eligible for FIDO + // authentication. + base::HistogramTester histogram_tester; + RecreateCreditCards(true /* include_local_credit_card */, + true /* include_masked_server_credit_card */, + true /* include_full_server_credit_card */); + SetFidoEligibility(true); + autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, + form.fields[0]); + std::string guid( + "10000000-0000-0000-0000-000000000002"); // masked server card + autofill_manager_->FillOrPreviewForm( + AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.back(), + autofill_manager_->MakeFrontendID(guid, std::string())); + // Preflight call is made only if a masked server card is available and the + // user is eligible for FIDO authentication (except iOS). +#if defined(OS_IOS) + histogram_tester.ExpectTotalCount(preflight_call_metric, 0); + histogram_tester.ExpectTotalCount(preflight_latency_metric, 0); +#else + histogram_tester.ExpectTotalCount(preflight_call_metric, 1); + histogram_tester.ExpectTotalCount(preflight_latency_metric, 1); +#endif + } +} + // Test that we log submitted form events for credit cards. TEST_F(AutofillMetricsTest, CreditCardGetRealPanDuration) { // Creating masked card @@ -4400,12 +4560,10 @@ TEST_P(AutofillMetricsIFrameTest, SubmissionSource::FORM_SUBMISSION); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", - FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_WRONG_SIZE_CARD, - 1); + FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_WRONG_SIZE_CARD, 1); histogram_tester.ExpectBucketCount( credit_card_form_events_frame_histogram_, - FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_WRONG_SIZE_CARD, - 1); + FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_WRONG_SIZE_CARD, 1); } TEST_P(AutofillMetricsIFrameTest, @@ -4447,12 +4605,10 @@ TEST_P(AutofillMetricsIFrameTest, SubmissionSource::FORM_SUBMISSION); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", - FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_FAIL_LUHN_CHECK_CARD, - 1); + FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_FAIL_LUHN_CHECK_CARD, 1); histogram_tester.ExpectBucketCount( credit_card_form_events_frame_histogram_, - FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_FAIL_LUHN_CHECK_CARD, - 1); + FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_FAIL_LUHN_CHECK_CARD, 1); } TEST_P(AutofillMetricsIFrameTest, @@ -4495,12 +4651,10 @@ TEST_P(AutofillMetricsIFrameTest, SubmissionSource::FORM_SUBMISSION); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", - FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_UNKNOWN_CARD, - 1); + FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_UNKNOWN_CARD, 1); histogram_tester.ExpectBucketCount( credit_card_form_events_frame_histogram_, - FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_UNKNOWN_CARD, - 1); + FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_UNKNOWN_CARD, 1); } TEST_P(AutofillMetricsIFrameTest, @@ -4543,12 +4697,10 @@ TEST_P(AutofillMetricsIFrameTest, SubmissionSource::FORM_SUBMISSION); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", - FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_KNOWN_CARD, - 1); + FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_KNOWN_CARD, 1); histogram_tester.ExpectBucketCount( credit_card_form_events_frame_histogram_, - FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_KNOWN_CARD, - 1); + FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_KNOWN_CARD, 1); } TEST_P(AutofillMetricsIFrameTest, @@ -4596,27 +4748,22 @@ TEST_P(AutofillMetricsIFrameTest, SubmissionSource::FORM_SUBMISSION); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", - FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_KNOWN_CARD, - 0); + FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_KNOWN_CARD, 0); histogram_tester.ExpectBucketCount( credit_card_form_events_frame_histogram_, - FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_KNOWN_CARD, - 0); + FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_KNOWN_CARD, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", - FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_UNKNOWN_CARD, - 0); + FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_UNKNOWN_CARD, 0); histogram_tester.ExpectBucketCount( credit_card_form_events_frame_histogram_, - FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_KNOWN_CARD, - 0); + FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_KNOWN_CARD, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_NO_CARD, 0); histogram_tester.ExpectBucketCount( credit_card_form_events_frame_histogram_, - FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_KNOWN_CARD, - 0); + FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_KNOWN_CARD, 0); } TEST_F(AutofillMetricsTest, ShouldNotLogFormEventNoCardForAddressForm) { @@ -5020,12 +5167,10 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) { FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", - FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, - 0); + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, 0); histogram_tester.ExpectBucketCount( credit_card_form_events_frame_histogram_, - FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, - 0); + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, 0); histogram_tester.ExpectBucketCount("Autofill.FormEvents.CreditCard", FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1); @@ -5052,12 +5197,10 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) { FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", - FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, - 0); + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, 0); histogram_tester.ExpectBucketCount( credit_card_form_events_frame_histogram_, - FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, - 0); + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, 0); } // Reset the autofill manager state and purge UKM logs. @@ -5098,12 +5241,10 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) { FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", - FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, - 0); + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, 0); histogram_tester.ExpectBucketCount( credit_card_form_events_frame_histogram_, - FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, - 0); + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0); @@ -5130,12 +5271,10 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) { FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", - FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, - 0); + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, 0); histogram_tester.ExpectBucketCount( credit_card_form_events_frame_histogram_, - FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, - 0); + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, 0); VerifyUkm( test_ukm_recorder_, form, UkmSuggestionsShownType::kEntryName, @@ -5364,12 +5503,10 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardWillSubmitFormEvents) { FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", - FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, - 0); + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, 0); histogram_tester.ExpectBucketCount( credit_card_form_events_frame_histogram_, - FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, - 0); + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, 0); histogram_tester.ExpectBucketCount("Autofill.FormEvents.CreditCard", FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1); @@ -5396,12 +5533,10 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardWillSubmitFormEvents) { FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", - FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, - 0); + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, 0); histogram_tester.ExpectBucketCount( credit_card_form_events_frame_histogram_, - FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, - 0); + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, 0); } // Reset the autofill manager state. @@ -5441,12 +5576,10 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardWillSubmitFormEvents) { FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", - FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, - 0); + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, 0); histogram_tester.ExpectBucketCount( credit_card_form_events_frame_histogram_, - FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, - 0); + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0); @@ -5473,12 +5606,10 @@ TEST_P(AutofillMetricsIFrameTest, CreditCardWillSubmitFormEvents) { FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", - FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, - 0); + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, 0); histogram_tester.ExpectBucketCount( credit_card_form_events_frame_histogram_, - FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, - 0); + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, 0); } } @@ -6167,8 +6298,7 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) { FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", - FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, - 0); + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0); @@ -6183,8 +6313,7 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) { FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", - FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, - 0); + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, 0); // Check if FormEvent UKM is logged properly auto entries = @@ -6308,8 +6437,7 @@ TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) { FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", - FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, - 0); + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0); @@ -6324,8 +6452,7 @@ TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) { FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", - FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, - 0); + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, 0); // Check if FormEvent UKM is logged properly auto entries = test_ukm_recorder_->GetEntriesByName(UkmFormEventType::kEntryName); @@ -6358,8 +6485,7 @@ TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) { FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", - FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, - 0); + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0); @@ -6374,8 +6500,7 @@ TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) { FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", - FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, - 0); + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, 0); // Check if FormEvent UKM is logged properly auto entries = test_ukm_recorder_->GetEntriesByName(UkmFormEventType::kEntryName); @@ -6566,7 +6691,8 @@ TEST_F(AutofillMetricsTest, AutofillIsDisabledAtPageLoad) { TEST_F(AutofillMetricsTest, DaysSinceLastUse_CreditCard) { base::HistogramTester histogram_tester; CreditCard credit_card; - credit_card.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(21)); + credit_card.set_use_date(AutofillClock::Now() - + base::TimeDelta::FromDays(21)); credit_card.RecordAndLogUse(); histogram_tester.ExpectBucketCount("Autofill.DaysSinceLastUse.CreditCard", 21, 1); @@ -6576,7 +6702,7 @@ TEST_F(AutofillMetricsTest, DaysSinceLastUse_CreditCard) { TEST_F(AutofillMetricsTest, DaysSinceLastUse_Profile) { base::HistogramTester histogram_tester; AutofillProfile profile; - profile.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(13)); + profile.set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(13)); profile.RecordAndLogUse(); histogram_tester.ExpectBucketCount("Autofill.DaysSinceLastUse.Profile", 13, 1); @@ -6605,7 +6731,7 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) { // Expect no notifications when the form is first seen. { base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, TimeTicks::Now()); + autofill_manager_->OnFormsSeen(forms, AutofillTickClock::NowTicks()); histogram_tester.ExpectTotalCount("Autofill.FormSubmittedState", 0); VerifyDeveloperEngagementUkm( @@ -6911,7 +7037,7 @@ TEST_F( { base::HistogramTester histogram_tester; base::UserActionTester user_action_tester; - autofill_manager_->OnFormsSeen(forms, TimeTicks::Now()); + autofill_manager_->OnFormsSeen(forms, AutofillTickClock::NowTicks()); VerifyDeveloperEngagementUkm( test_ukm_recorder_, form, /*is_for_credit_card=*/false, {FormType::ADDRESS_FORM}, @@ -7463,7 +7589,12 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_AddressForm) { } // Verify that we correctly log metrics tracking the duration of form fill. +// TODO(crbug.com/1009364) Test is flake on many builders. TEST_F(AutofillMetricsTest, FormFillDuration) { + base::TimeTicks now = AutofillTickClock::NowTicks(); + TestAutofillTickClock test_clock; + test_clock.SetNowTicks(now); + // Load a fillable form. FormData form; form.name = ASCIIToUTF16("TestForm"); @@ -7502,8 +7633,13 @@ TEST_F(AutofillMetricsTest, FormFillDuration) { // Expect only form load metrics to be logged if the form is submitted without // user interaction. { + SCOPED_TRACE("Test 1"); base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, TimeTicks::Now()); + autofill_manager_->OnFormsSeen(forms, AutofillTickClock::NowTicks()); + base::TimeTicks parse_time = autofill_manager_->form_structures() + .begin() + ->second->form_parsed_timestamp(); + test_clock.SetNowTicks(parse_time + base::TimeDelta::FromMicroseconds(17)); autofill_manager_->OnFormSubmitted(form, false, SubmissionSource::FORM_SUBMISSION); @@ -7521,14 +7657,16 @@ TEST_F(AutofillMetricsTest, FormFillDuration) { // Expect metric to be logged if the user manually edited a form field. { + SCOPED_TRACE("Test 2"); base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, TimeTicks::Now()); + autofill_manager_->OnFormsSeen(forms, AutofillTickClock::NowTicks()); base::TimeTicks parse_time = autofill_manager_->form_structures() .begin() ->second->form_parsed_timestamp(); autofill_manager_->OnTextFieldDidChange( form, form.fields.front(), gfx::RectF(), parse_time + base::TimeDelta::FromMicroseconds(3)); + test_clock.SetNowTicks(parse_time + base::TimeDelta::FromMicroseconds(17)); autofill_manager_->OnFormSubmitted(form, false, SubmissionSource::FORM_SUBMISSION); @@ -7548,13 +7686,15 @@ TEST_F(AutofillMetricsTest, FormFillDuration) { // Expect metric to be logged if the user autofilled the form. form.fields[0].is_autofilled = true; { + SCOPED_TRACE("Test 3"); base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, TimeTicks::Now()); + autofill_manager_->OnFormsSeen(forms, AutofillTickClock::NowTicks()); base::TimeTicks parse_time = autofill_manager_->form_structures() .begin() ->second->form_parsed_timestamp(); autofill_manager_->OnDidFillAutofillFormData( form, parse_time + base::TimeDelta::FromMicroseconds(5)); + test_clock.SetNowTicks(parse_time + base::TimeDelta::FromMicroseconds(17)); autofill_manager_->OnFormSubmitted(form, false, SubmissionSource::FORM_SUBMISSION); @@ -7575,17 +7715,20 @@ TEST_F(AutofillMetricsTest, FormFillDuration) { // and autofilled others. Messages can arrive out of order, so make sure they // take precedence appropriately. { + SCOPED_TRACE("Test 4"); base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, TimeTicks::Now()); + autofill_manager_->OnFormsSeen(forms, AutofillTickClock::NowTicks()); base::TimeTicks parse_time = autofill_manager_->form_structures() .begin() ->second->form_parsed_timestamp(); autofill_manager_->OnDidFillAutofillFormData( form, parse_time + base::TimeDelta::FromMicroseconds(5)); + autofill_manager_->OnTextFieldDidChange( form, form.fields.front(), gfx::RectF(), parse_time + base::TimeDelta::FromMicroseconds(3)); + test_clock.SetNowTicks(parse_time + base::TimeDelta::FromMicroseconds(17)); autofill_manager_->OnFormSubmitted(form, false, SubmissionSource::FORM_SUBMISSION); @@ -7605,17 +7748,19 @@ TEST_F(AutofillMetricsTest, FormFillDuration) { // Make sure that loading another form doesn't affect metrics from the first // form. { + SCOPED_TRACE("Test 5"); base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, TimeTicks::Now()); + autofill_manager_->OnFormsSeen(forms, AutofillTickClock::NowTicks()); base::TimeTicks parse_time = autofill_manager_->form_structures() .begin() ->second->form_parsed_timestamp(); - autofill_manager_->OnFormsSeen(second_forms, TimeTicks::Now()); + autofill_manager_->OnFormsSeen(second_forms, AutofillTickClock::NowTicks()); autofill_manager_->OnDidFillAutofillFormData( form, parse_time + base::TimeDelta::FromMicroseconds(5)); autofill_manager_->OnTextFieldDidChange( form, form.fields.front(), gfx::RectF(), parse_time + base::TimeDelta::FromMicroseconds(3)); + test_clock.SetNowTicks(parse_time + base::TimeDelta::FromMicroseconds(17)); autofill_manager_->OnFormSubmitted(form, false, SubmissionSource::FORM_SUBMISSION); @@ -7635,14 +7780,16 @@ TEST_F(AutofillMetricsTest, FormFillDuration) { // Make sure that submitting a form that was loaded later will report the // later loading time. { + SCOPED_TRACE("Test 6"); base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, TimeTicks::Now()); - autofill_manager_->OnFormsSeen(second_forms, TimeTicks::Now()); + autofill_manager_->OnFormsSeen(forms, AutofillTickClock::NowTicks()); + autofill_manager_->OnFormsSeen(second_forms, AutofillTickClock::NowTicks()); base::TimeTicks parse_time{}; for (const auto& kv : autofill_manager_->form_structures()) { if (kv.second->form_parsed_timestamp() > parse_time) parse_time = kv.second->form_parsed_timestamp(); } + test_clock.SetNowTicks(parse_time + base::TimeDelta::FromMicroseconds(17)); autofill_manager_->OnFormSubmitted(second_form, false, SubmissionSource::FORM_SUBMISSION); @@ -7941,7 +8088,7 @@ TEST_F(AutofillMetricsTest, ProfileActionOnFormSubmitted) { // Expect to log NEW_PROFILE_CREATED for the metric since a new profile is // submitted. - autofill_manager_->OnFormsSeen(forms, TimeTicks::Now()); + autofill_manager_->OnFormsSeen(forms, AutofillTickClock::NowTicks()); autofill_manager_->OnFormSubmitted(form, false, SubmissionSource::FORM_SUBMISSION); histogram_tester.ExpectBucketCount("Autofill.ProfileActionOnFormSubmitted", @@ -7954,7 +8101,7 @@ TEST_F(AutofillMetricsTest, ProfileActionOnFormSubmitted) { // Expect to log EXISTING_PROFILE_USED for the metric since the same profile // is submitted. - autofill_manager_->OnFormsSeen(second_forms, TimeTicks::Now()); + autofill_manager_->OnFormsSeen(second_forms, AutofillTickClock::NowTicks()); autofill_manager_->OnFormSubmitted(second_form, false, SubmissionSource::FORM_SUBMISSION); histogram_tester.ExpectBucketCount("Autofill.ProfileActionOnFormSubmitted", @@ -7967,7 +8114,7 @@ TEST_F(AutofillMetricsTest, ProfileActionOnFormSubmitted) { // Expect to log NEW_PROFILE_CREATED for the metric since a new profile is // submitted. - autofill_manager_->OnFormsSeen(third_forms, TimeTicks::Now()); + autofill_manager_->OnFormsSeen(third_forms, AutofillTickClock::NowTicks()); autofill_manager_->OnFormSubmitted(third_form, false, SubmissionSource::FORM_SUBMISSION); histogram_tester.ExpectBucketCount("Autofill.ProfileActionOnFormSubmitted", @@ -7980,7 +8127,7 @@ TEST_F(AutofillMetricsTest, ProfileActionOnFormSubmitted) { // Expect to log EXISTING_PROFILE_UPDATED for the metric since the profile was // updated. - autofill_manager_->OnFormsSeen(fourth_forms, TimeTicks::Now()); + autofill_manager_->OnFormsSeen(fourth_forms, AutofillTickClock::NowTicks()); autofill_manager_->OnFormSubmitted(fourth_form, false, SubmissionSource::FORM_SUBMISSION); histogram_tester.ExpectBucketCount("Autofill.ProfileActionOnFormSubmitted", @@ -8434,9 +8581,9 @@ TEST_F(AutofillMetricsTest, LogUserHappinessBySecurityLevel) { base::HistogramTester histogram_tester; AutofillMetrics::LogUserHappinessBySecurityLevel( AutofillMetrics::FIELD_WAS_AUTOFILLED, PASSWORD_FORM, - security_state::SecurityLevel::HTTP_SHOW_WARNING); + security_state::SecurityLevel::WARNING); histogram_tester.ExpectBucketCount( - "Autofill.UserHappiness.Password.HTTP_SHOW_WARNING", + "Autofill.UserHappiness.Password.WARNING", AutofillMetrics::FIELD_WAS_AUTOFILLED, 1); } @@ -8496,16 +8643,14 @@ TEST_F(AutofillMetricsTest, LogUserHappinessBySecurityLevel_FromFormEvents) { // Simulate suggestions shown twice with separate popups. { base::HistogramTester histogram_tester; - autofill_client_.set_security_level( - security_state::SecurityLevel::HTTP_SHOW_WARNING); + autofill_client_.set_security_level(security_state::SecurityLevel::WARNING); autofill_manager_->DidShowSuggestions(true, form, field); autofill_manager_->DidShowSuggestions(true, form, field); - histogram_tester.ExpectBucketCount( - "Autofill.UserHappiness.Address.HTTP_SHOW_WARNING", - AutofillMetrics::SUGGESTIONS_SHOWN, 2); - histogram_tester.ExpectBucketCount( - "Autofill.UserHappiness.Address.HTTP_SHOW_WARNING", - AutofillMetrics::SUGGESTIONS_SHOWN_ONCE, 1); + histogram_tester.ExpectBucketCount("Autofill.UserHappiness.Address.WARNING", + AutofillMetrics::SUGGESTIONS_SHOWN, 2); + histogram_tester.ExpectBucketCount("Autofill.UserHappiness.Address.WARNING", + AutofillMetrics::SUGGESTIONS_SHOWN_ONCE, + 1); } } @@ -8751,9 +8896,9 @@ TEST_F(AutofillMetricsTest, LogSaveCardPromptMetricBySecurityLevel) { base::HistogramTester histogram_tester; AutofillMetrics::LogSaveCardPromptMetricBySecurityLevel( AutofillMetrics::SAVE_CARD_PROMPT_END_ACCEPTED, /*is_uploading=*/true, - security_state::SecurityLevel::HTTP_SHOW_WARNING); + security_state::SecurityLevel::WARNING); histogram_tester.ExpectBucketCount( - "Autofill.SaveCreditCardPrompt.Upload.HTTP_SHOW_WARNING", + "Autofill.SaveCreditCardPrompt.Upload.WARNING", AutofillMetrics::SAVE_CARD_PROMPT_END_ACCEPTED, 1); } @@ -8914,7 +9059,7 @@ TEST_F(AutofillMetricsTest, FormEventMetrics_BySyncState) { FormData form; FormStructure form_structure(form); std::vector<FormData> forms(1, form); - autofill_manager_->OnFormsSeen(forms, TimeTicks::Now()); + autofill_manager_->OnFormsSeen(forms, AutofillTickClock::NowTicks()); autofill_manager_->Reset(); { diff --git a/chromium/components/autofill/core/browser/autofill_test_utils.cc b/chromium/components/autofill/core/browser/autofill_test_utils.cc index 9ff57ce1937..727f5b9aa82 100644 --- a/chromium/components/autofill/core/browser/autofill_test_utils.cc +++ b/chromium/components/autofill/core/browser/autofill_test_utils.cc @@ -8,6 +8,7 @@ #include "base/guid.h" #include "base/rand_util.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" @@ -17,6 +18,7 @@ #include "components/autofill/core/browser/data_model/credit_card.h" #include "components/autofill/core/browser/field_types.h" #include "components/autofill/core/browser/webdata/autofill_table.h" +#include "components/autofill/core/common/autofill_clock.h" #include "components/autofill/core/common/autofill_constants.h" #include "components/autofill/core/common/autofill_prefs.h" #include "components/autofill/core/common/form_data.h" @@ -118,6 +120,7 @@ void CreateTestAddressFormData(FormData* form, mojom::ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)}; form->url = GURL("http://myform.com/form.html"); form->action = GURL("http://myform.com/submit.html"); + form->is_action_empty = true; form->main_frame_origin = url::Origin::Create(GURL("https://myform_root.com/form.html")); types->clear(); @@ -254,8 +257,9 @@ void CreateTestCreditCardFormData(FormData* form, form->fields.push_back(field); } -inline void check_and_set( - FormGroup* profile, ServerFieldType type, const char* value) { +inline void check_and_set(FormGroup* profile, + ServerFieldType type, + const char* value) { if (value) profile->SetRawInfo(type, base::UTF8ToUTF16(value)); } @@ -278,34 +282,16 @@ AutofillProfile GetFullValidProfileForChina() { AutofillProfile GetFullProfile() { AutofillProfile profile(base::GenerateGUID(), kEmptyOrigin); - SetProfileInfo(&profile, - "John", - "H.", - "Doe", - "johndoe@hades.com", - "Underworld", - "666 Erebus St.", - "Apt 8", - "Elysium", "CA", - "91111", - "US", - "16502111111"); + SetProfileInfo(&profile, "John", "H.", "Doe", "johndoe@hades.com", + "Underworld", "666 Erebus St.", "Apt 8", "Elysium", "CA", + "91111", "US", "16502111111"); return profile; } AutofillProfile GetFullProfile2() { AutofillProfile profile(base::GenerateGUID(), kEmptyOrigin); - SetProfileInfo(&profile, - "Jane", - "A.", - "Smith", - "jsmith@example.com", - "ACME", - "123 Main Street", - "Unit 1", - "Greensdale", "MI", - "48838", - "US", + SetProfileInfo(&profile, "Jane", "A.", "Smith", "jsmith@example.com", "ACME", + "123 Main Street", "Unit 1", "Greensdale", "MI", "48838", "US", "13105557889"); return profile; } @@ -466,7 +452,7 @@ CreditCard GetRandomCreditCard(CreditCard::RecordType record_type) { }; constexpr size_t kNumNetworks = sizeof(kNetworks) / sizeof(kNetworks[0]); base::Time::Exploded now; - base::Time::Now().LocalExplode(&now); + AutofillClock::Now().LocalExplode(&now); CreditCard credit_card = (record_type == CreditCard::LOCAL_CARD) @@ -541,16 +527,23 @@ void SetProfileInfo(AutofillProfile* profile, } void SetProfileInfoWithGuid(AutofillProfile* profile, - const char* guid, const char* first_name, const char* middle_name, - const char* last_name, const char* email, const char* company, - const char* address1, const char* address2, const char* city, - const char* state, const char* zipcode, const char* country, - const char* phone) { + const char* guid, + const char* first_name, + const char* middle_name, + const char* last_name, + const char* email, + const char* company, + const char* address1, + const char* address2, + const char* city, + const char* state, + const char* zipcode, + const char* country, + const char* phone) { if (guid) profile->set_guid(guid); - SetProfileInfo(profile, first_name, middle_name, last_name, email, - company, address1, address2, city, state, zipcode, country, - phone); + SetProfileInfo(profile, first_name, middle_name, last_name, email, company, + address1, address2, city, state, zipcode, country, phone); } void SetCreditCardInfo(CreditCard* credit_card, @@ -725,20 +718,25 @@ std::string ObfuscatedCardDigitsAsUTF8(const std::string& str) { internal::GetObfuscatedStringForCardDigits(base::ASCIIToUTF16(str))); } +std::string NextMonth() { + base::Time::Exploded now; + base::Time::Now().LocalExplode(&now); + return base::NumberToString(now.month % 12 + 1); +} std::string LastYear() { base::Time::Exploded now; base::Time::Now().LocalExplode(&now); - return std::to_string(now.year - 1); + return base::NumberToString(now.year - 1); } std::string NextYear() { base::Time::Exploded now; base::Time::Now().LocalExplode(&now); - return std::to_string(now.year + 1); + return base::NumberToString(now.year + 1); } std::string TenYearsFromNow() { base::Time::Exploded now; base::Time::Now().LocalExplode(&now); - return std::to_string(now.year + 10); + return base::NumberToString(now.year + 10); } } // namespace test diff --git a/chromium/components/autofill/core/browser/autofill_test_utils.h b/chromium/components/autofill/core/browser/autofill_test_utils.h index 678a211d5d4..db80caea7a8 100644 --- a/chromium/components/autofill/core/browser/autofill_test_utils.h +++ b/chromium/components/autofill/core/browser/autofill_test_utils.h @@ -176,11 +176,19 @@ void SetProfileInfo(AutofillProfile* profile, const char* phone); void SetProfileInfoWithGuid(AutofillProfile* profile, - const char* guid, const char* first_name, const char* middle_name, - const char* last_name, const char* email, const char* company, - const char* address1, const char* address2, const char* city, - const char* state, const char* zipcode, const char* country, - const char* phone); + const char* guid, + const char* first_name, + const char* middle_name, + const char* last_name, + const char* email, + const char* company, + const char* address1, + const char* address2, + const char* city, + const char* state, + const char* zipcode, + const char* country, + const char* phone); // A unit testing utility that is common to a number of the Autofill unit // tests. |SetCreditCardInfo| provides a quick way to populate a credit card @@ -258,6 +266,7 @@ void GenerateTestAutofillPopup( std::string ObfuscatedCardDigitsAsUTF8(const std::string& str); +std::string NextMonth(); std::string LastYear(); std::string NextYear(); std::string TenYearsFromNow(); diff --git a/chromium/components/autofill/core/browser/autofill_type.cc b/chromium/components/autofill/core/browser/autofill_type.cc index d141a0dbcd3..4d0d4241c79 100644 --- a/chromium/components/autofill/core/browser/autofill_type.cc +++ b/chromium/components/autofill/core/browser/autofill_type.cc @@ -56,6 +56,10 @@ FieldTypeGroup GroupTypeOfServerFieldType(ServerFieldType field_type) { case ADDRESS_HOME_STREET_ADDRESS: case ADDRESS_HOME_SORTING_CODE: case ADDRESS_HOME_DEPENDENT_LOCALITY: + case ADDRESS_HOME_STREET: + case ADDRESS_HOME_HOUSE_NUMBER: + case ADDRESS_HOME_FLOOR: + case ADDRESS_HOME_OTHER_SUBUNIT: return ADDRESS_HOME; case ADDRESS_BILLING_LINE1: @@ -439,78 +443,6 @@ ServerFieldType AutofillType::GetStorableType() const { return UNKNOWN_TYPE; } -// static -ServerFieldType AutofillType::GetEquivalentBillingFieldType( - ServerFieldType field_type) { - switch (field_type) { - case ADDRESS_HOME_LINE1: - return ADDRESS_BILLING_LINE1; - - case ADDRESS_HOME_LINE2: - return ADDRESS_BILLING_LINE2; - - case ADDRESS_HOME_APT_NUM: - return ADDRESS_BILLING_APT_NUM; - - case ADDRESS_HOME_CITY: - return ADDRESS_BILLING_CITY; - - case ADDRESS_HOME_STATE: - return ADDRESS_BILLING_STATE; - - case ADDRESS_HOME_ZIP: - return ADDRESS_BILLING_ZIP; - - case ADDRESS_HOME_COUNTRY: - return ADDRESS_BILLING_COUNTRY; - - case ADDRESS_HOME_STREET_ADDRESS: - return ADDRESS_BILLING_STREET_ADDRESS; - - case ADDRESS_HOME_SORTING_CODE: - return ADDRESS_BILLING_SORTING_CODE; - - case ADDRESS_HOME_DEPENDENT_LOCALITY: - return ADDRESS_BILLING_DEPENDENT_LOCALITY; - - case PHONE_HOME_WHOLE_NUMBER: - return PHONE_BILLING_WHOLE_NUMBER; - - case PHONE_HOME_NUMBER: - return PHONE_BILLING_NUMBER; - - case PHONE_HOME_CITY_CODE: - return PHONE_BILLING_CITY_CODE; - - case PHONE_HOME_COUNTRY_CODE: - return PHONE_BILLING_COUNTRY_CODE; - - case PHONE_HOME_CITY_AND_NUMBER: - return PHONE_BILLING_CITY_AND_NUMBER; - - case NAME_FIRST: - return NAME_BILLING_FIRST; - - case NAME_MIDDLE: - return NAME_BILLING_MIDDLE; - - case NAME_LAST: - return NAME_BILLING_LAST; - - case NAME_MIDDLE_INITIAL: - return NAME_BILLING_MIDDLE_INITIAL; - - case NAME_FULL: - return NAME_BILLING_FULL; - - case NAME_SUFFIX: - return NAME_BILLING_SUFFIX; - - default: - return field_type; - } -} - std::string AutofillType::ToString() const { if (IsUnknown()) return "UNKNOWN_TYPE"; @@ -788,6 +720,14 @@ std::string AutofillType::ServerFieldTypeToString(ServerFieldType type) { return "NOT_USERNAME"; case UPI_VPA: return "UPI_VPA"; + case ADDRESS_HOME_STREET: + return "ADDRESS_HOME_STREET"; + case ADDRESS_HOME_HOUSE_NUMBER: + return "ADDRESS_HOME_HOUSE_NUMBER"; + case ADDRESS_HOME_FLOOR: + return "ADDRESS_HOME_FLOOR"; + case ADDRESS_HOME_OTHER_SUBUNIT: + return "ADDRESS_HOME_OTHER_SUBUNIT"; case AMBIGUOUS_TYPE: return "AMBIGUOUS_TYPE"; case MAX_VALID_FIELD_TYPE: diff --git a/chromium/components/autofill/core/browser/autofill_type.h b/chromium/components/autofill/core/browser/autofill_type.h index 893567a9214..0e65d1fa05a 100644 --- a/chromium/components/autofill/core/browser/autofill_type.h +++ b/chromium/components/autofill/core/browser/autofill_type.h @@ -24,7 +24,7 @@ FieldTypeGroup GroupTypeOfHtmlFieldType(HtmlFieldType field_type, // and for associating form fields with form values in the Web Database. class AutofillType { public: - explicit AutofillType(ServerFieldType field_type); + explicit AutofillType(ServerFieldType field_type = NO_SERVER_DATA); AutofillType(HtmlFieldType field_type, HtmlFieldMode mode); AutofillType(const AutofillType& autofill_type) = default; AutofillType& operator=(const AutofillType& autofill_type) = default; @@ -48,11 +48,6 @@ class AutofillType { // Serializes |this| type to a string. std::string ToString() const; - // Maps |field_type| to the corresponding billing field type if the field type - // is an address, name, or phone number type. - static ServerFieldType GetEquivalentBillingFieldType( - ServerFieldType field_type); - // Translates the ServerFieldType values into the corresponding strings. static std::string ServerFieldTypeToString(ServerFieldType type); diff --git a/chromium/components/autofill/core/browser/data_driven_test.cc b/chromium/components/autofill/core/browser/data_driven_test.cc index d30b91f2432..2a399402bf5 100644 --- a/chromium/components/autofill/core/browser/data_driven_test.cc +++ b/chromium/components/autofill/core/browser/data_driven_test.cc @@ -9,6 +9,7 @@ #include "base/strings/string_util.h" #include "base/threading/thread_restrictions.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/re2/src/re2/re2.h" namespace autofill { namespace { @@ -30,6 +31,24 @@ bool WriteFile(const base::FilePath& file, const std::string& content) { return write_size == static_cast<int>(content.length()); } +// Removes lines starting with (optional) whitespace and a #. +void StripComments(std::string* content) { + RE2::GlobalReplace( + content, + // Enable multi-line mode, ^ and $ match begin/end line in addition to + // begin/end text. + "(?m)" + // Search for start of lines (^), ignore spaces (\\s*), and then look for + // '#'. + "^\\s*#" + // Consume all characters (.*) until end of line ($). + ".*$" + // Consume the line wrapping so that the entire line is gone. + "[\\r\\n]*", + // Replace entire line with empty string. + ""); +} + } // namespace void DataDrivenTest::RunDataDrivenTest( @@ -81,6 +100,8 @@ void DataDrivenTest::RunOneDataDrivenTest( ASSERT_TRUE(WriteFile(output_file, output)); return; } + // Remove comment lines (lead by '#' character). + StripComments(&output_file_contents); if (is_expected_to_pass) { EXPECT_EQ(output_file_contents, output); diff --git a/chromium/components/autofill/core/browser/data_model/autofill_data_model_unittest.cc b/chromium/components/autofill/core/browser/data_model/autofill_data_model_unittest.cc index b8762af5f1c..b24e914c5d2 100644 --- a/chromium/components/autofill/core/browser/data_model/autofill_data_model_unittest.cc +++ b/chromium/components/autofill/core/browser/data_model/autofill_data_model_unittest.cc @@ -11,6 +11,7 @@ #include "base/time/time.h" #include "components/autofill/core/browser/data_model/autofill_metadata.h" #include "components/autofill/core/browser/test_autofill_clock.h" +#include "components/autofill/core/common/autofill_clock.h" #include "components/autofill/core/common/autofill_constants.h" #include "testing/gtest/include/gtest/gtest.h" @@ -120,7 +121,7 @@ struct HasGreaterFrecencyThanTestCase { Expectation expectation; }; -base::Time now = base::Time::Now(); +base::Time now = AutofillClock::Now(); class HasGreaterFrecencyThanTest : public testing::TestWithParam<HasGreaterFrecencyThanTestCase> {}; diff --git a/chromium/components/autofill/core/browser/data_model/autofill_profile.cc b/chromium/components/autofill/core/browser/data_model/autofill_profile.cc index 125a506ba17..60aeae68441 100644 --- a/chromium/components/autofill/core/browser/data_model/autofill_profile.cc +++ b/chromium/components/autofill/core/browser/data_model/autofill_profile.cc @@ -482,14 +482,6 @@ bool AutofillProfile::operator!=(const AutofillProfile& profile) const { return !operator==(profile); } -bool AutofillProfile::IsSubsetOf(const AutofillProfile& profile, - const std::string& app_locale) const { - ServerFieldTypeSet types; - GetSupportedTypes(&types); - return IsSubsetOfForFieldSet(AutofillProfileComparator(app_locale), profile, - app_locale, types); -} - bool AutofillProfile::IsSubsetOfForFieldSet( const AutofillProfileComparator& comparator, const AutofillProfile& profile, diff --git a/chromium/components/autofill/core/browser/data_model/autofill_profile.h b/chromium/components/autofill/core/browser/data_model/autofill_profile.h index 768dc5103d4..a984548e313 100644 --- a/chromium/components/autofill/core/browser/data_model/autofill_profile.h +++ b/chromium/components/autofill/core/browser/data_model/autofill_profile.h @@ -124,10 +124,6 @@ class AutofillProfile : public AutofillDataModel { bool operator==(const AutofillProfile& profile) const; virtual bool operator!=(const AutofillProfile& profile) const; - // Returns true if this AutofillProfile's data is a subset of |profile|'s. - bool IsSubsetOf(const AutofillProfile& profile, - const std::string& app_locale) const; - // Like IsSubsetOf, but considers only the given |types|. bool IsSubsetOfForFieldSet(const AutofillProfileComparator& comparator, const AutofillProfile& profile, diff --git a/chromium/components/autofill/core/browser/data_model/autofill_profile_comparator.cc b/chromium/components/autofill/core/browser/data_model/autofill_profile_comparator.cc index 9b41b0b77d0..ac578d38a0f 100644 --- a/chromium/components/autofill/core/browser/data_model/autofill_profile_comparator.cc +++ b/chromium/components/autofill/core/browser/data_model/autofill_profile_comparator.cc @@ -16,8 +16,10 @@ #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/address_rewriter.h" #include "components/autofill/core/browser/autofill_data_util.h" +#include "components/autofill/core/browser/autofill_metrics.h" #include "components/autofill/core/browser/geo/autofill_country.h" #include "components/autofill/core/browser/geo/state_names.h" +#include "components/autofill/core/common/autofill_clock.h" #include "third_party/libphonenumber/phonenumber_api.h" using base::UTF16ToUTF8; @@ -829,6 +831,70 @@ bool AutofillProfileComparator::MergeAddresses(const AutofillProfile& p1, } // static +std::string AutofillProfileComparator::MergeProfile( + const AutofillProfile& new_profile, + std::vector<std::unique_ptr<AutofillProfile>>* existing_profiles, + const std::string& app_locale, + std::vector<AutofillProfile>* merged_profiles) { + merged_profiles->clear(); + + // Sort the existing profiles in decreasing order of frecency, so the "best" + // profiles are checked first. Put the verified profiles last so the non + // verified profiles get deduped among themselves before reaching the verified + // profiles. + // TODO(crbug.com/620521): Remove the check for verified from the sort. + base::Time comparison_time = AutofillClock::Now(); + std::sort(existing_profiles->begin(), existing_profiles->end(), + [comparison_time](const std::unique_ptr<AutofillProfile>& a, + const std::unique_ptr<AutofillProfile>& b) { + if (a->IsVerified() != b->IsVerified()) + return !a->IsVerified(); + return a->HasGreaterFrecencyThan(b.get(), comparison_time); + }); + + // Set to true if |existing_profiles| already contains an equivalent profile. + bool matching_profile_found = false; + std::string guid = new_profile.guid(); + + // If we have already saved this address, merge in any missing values. + // Only merge with the first match. Merging the new profile into the existing + // one preserves the validity of credit card's billing address reference. + AutofillProfileComparator comparator(app_locale); + for (const auto& existing_profile : *existing_profiles) { + if (!matching_profile_found && + comparator.AreMergeable(new_profile, *existing_profile) && + existing_profile->SaveAdditionalInfo(new_profile, app_locale)) { + // Unverified profiles should always be updated with the newer data, + // whereas verified profiles should only ever be overwritten by verified + // data. If an automatically aggregated profile would overwrite a + // verified profile, just drop it. + matching_profile_found = true; + guid = existing_profile->guid(); + + // We set the modification date so that immediate requests for profiles + // will properly reflect the fact that this profile has been modified + // recently. After writing to the database and refreshing the local copies + // the profile will have a very slightly newer time reflecting what's + // actually stored in the database. + existing_profile->set_modification_date(AutofillClock::Now()); + } + merged_profiles->push_back(*existing_profile); + } + + // If the new profile was not merged with an existing one, add it to the list. + if (!matching_profile_found) { + merged_profiles->push_back(new_profile); + // Similar to updating merged profiles above, set the modification date on + // new profiles. + merged_profiles->back().set_modification_date(AutofillClock::Now()); + AutofillMetrics::LogProfileActionOnFormSubmitted( + AutofillMetrics::NEW_PROFILE_CREATED); + } + + return guid; +} + +// static std::set<base::StringPiece16> AutofillProfileComparator::UniqueTokens( base::StringPiece16 s) { std::vector<base::StringPiece16> tokens = base::SplitStringPiece( diff --git a/chromium/components/autofill/core/browser/data_model/autofill_profile_comparator.h b/chromium/components/autofill/core/browser/data_model/autofill_profile_comparator.h index 127c828743b..525bf0703a2 100644 --- a/chromium/components/autofill/core/browser/data_model/autofill_profile_comparator.h +++ b/chromium/components/autofill/core/browser/data_model/autofill_profile_comparator.h @@ -138,6 +138,16 @@ class AutofillProfileComparator { // App locale used when this comparator instance was created. const std::string app_locale() const { return app_locale_; } + // Merges |new_profile| into one of the |existing_profiles| if possible; + // otherwise appends |new_profile| to the end of that list. Fills + // |merged_profiles| with the result. Returns the |guid| of the new or updated + // profile. + static std::string MergeProfile( + const AutofillProfile& new_profile, + std::vector<std::unique_ptr<AutofillProfile>>* existing_profiles, + const std::string& app_locale, + std::vector<AutofillProfile>* merged_profiles); + protected: // The result type returned by CompareTokens. enum CompareTokensResult { diff --git a/chromium/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc b/chromium/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc index af44b882387..445c4cafbc3 100644 --- a/chromium/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc +++ b/chromium/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc @@ -12,6 +12,7 @@ #include "components/autofill/core/browser/data_model/contact_info.h" #include "components/autofill/core/browser/field_types.h" #include "components/autofill/core/browser/geo/country_names.h" +#include "components/autofill/core/common/autofill_clock.h" #include "components/autofill/core/common/autofill_features.h" #include "testing/gtest/include/gtest/gtest.h" @@ -41,6 +42,7 @@ using autofill::PHONE_HOME_WHOLE_NUMBER; // Classes, Functions, and other Symbols using autofill::Address; +using autofill::AutofillClock; using autofill::AutofillProfile; using autofill::AutofillType; using autofill::CompanyInfo; @@ -785,15 +787,15 @@ TEST_F(AutofillProfileComparatorTest, MergeCJKNames) { // the most recent profile if there is a conflict. The ordering is // p1 > p2 > p3 > p4 > p5, with p1 being the most recent. AutofillProfile p1 = CreateProfileWithName(name1); - p1.set_use_date(base::Time::Now()); + p1.set_use_date(AutofillClock::Now()); AutofillProfile p2 = CreateProfileWithName(name2); - p2.set_use_date(base::Time::Now() - base::TimeDelta::FromHours(1)); + p2.set_use_date(AutofillClock::Now() - base::TimeDelta::FromHours(1)); AutofillProfile p3 = CreateProfileWithName(name3); - p3.set_use_date(base::Time::Now() - base::TimeDelta::FromHours(2)); + p3.set_use_date(AutofillClock::Now() - base::TimeDelta::FromHours(2)); AutofillProfile p4 = CreateProfileWithName(name4); - p4.set_use_date(base::Time::Now() - base::TimeDelta::FromHours(3)); + p4.set_use_date(AutofillClock::Now() - base::TimeDelta::FromHours(3)); AutofillProfile p5 = CreateProfileWithName(name5); - p5.set_use_date(base::Time::Now() - base::TimeDelta::FromHours(4)); + p5.set_use_date(AutofillClock::Now() - base::TimeDelta::FromHours(4)); AutofillProfile p6 = CreateProfileWithName(name6); AutofillProfile p7 = CreateProfileWithName(name7); @@ -831,7 +833,7 @@ TEST_F(AutofillProfileComparatorTest, MergeEmailAddresses) { EmailInfo email_a; email_a.SetRawInfo(EMAIL_ADDRESS, UTF8ToUTF16(kEmailA)); AutofillProfile profile_a = CreateProfileWithEmail(kEmailA); - profile_a.set_use_date(base::Time::Now()); + profile_a.set_use_date(AutofillClock::Now()); EmailInfo email_b; email_b.SetRawInfo(EMAIL_ADDRESS, UTF8ToUTF16(kEmailB)); @@ -859,7 +861,7 @@ TEST_F(AutofillProfileComparatorTest, MergeCompanyNames) { CompanyInfo company_a; company_a.SetRawInfo(COMPANY_NAME, UTF8ToUTF16(kCompanyA)); AutofillProfile profile_a = CreateProfileWithCompanyName(kCompanyA); - profile_a.set_use_date(base::Time::Now()); + profile_a.set_use_date(AutofillClock::Now()); // Company Name B is post_normalization identical to Company Name A. The use // date will be used to choose between them. @@ -879,7 +881,7 @@ TEST_F(AutofillProfileComparatorTest, MergeCompanyNames) { CompanyInfo company_d; company_d.SetRawInfo(COMPANY_NAME, UTF8ToUTF16(kCompanyD)); AutofillProfile profile_d = CreateProfileWithCompanyName(kCompanyD); - profile_a.set_use_date(base::Time::Now()); + profile_a.set_use_date(AutofillClock::Now()); MergeCompanyNamesAndExpect(profile_a, profile_a, company_a); MergeCompanyNamesAndExpect(profile_a, profile_b, company_b); diff --git a/chromium/components/autofill/core/browser/data_model/autofill_profile_unittest.cc b/chromium/components/autofill/core/browser/data_model/autofill_profile_unittest.cc index de503b8f649..58f336978d6 100644 --- a/chromium/components/autofill/core/browser/data_model/autofill_profile_unittest.cc +++ b/chromium/components/autofill/core/browser/data_model/autofill_profile_unittest.cc @@ -22,6 +22,7 @@ #include "components/autofill/core/browser/data_model/autofill_profile_comparator.h" #include "components/autofill/core/browser/field_types.h" #include "components/autofill/core/browser/test_autofill_clock.h" +#include "components/autofill/core/common/autofill_clock.h" #include "components/autofill/core/common/autofill_constants.h" #include "components/autofill/core/common/autofill_features.h" #include "components/autofill/core/common/form_field_data.h" @@ -673,31 +674,6 @@ TEST(AutofillProfileTest, CreateInferredLabelsFlattensMultiLineValues) { EXPECT_EQ(ASCIIToUTF16("88 Nowhere Ave., Apt. 42"), labels[0]); } -TEST(AutofillProfileTest, IsSubsetOfForProfiles) { - AutofillProfile profile1 = - AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(&profile1, "Genevieve", "", "Fox", - "genevieve@hotmail.com", "", "274 Main St", "", - "Northhampton", "MA", "01060", "US", ""); - - AutofillProfile profile2 = - AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(&profile2, "Genevieve", "", "Fox", - "genevieve@hotmail.com", "", "", "", "", "", "", "US", - ""); - - AutofillProfile profile3 = - AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin); - test::SetProfileInfo(&profile3, "Genevieve", "", "Fuller", - "genevieve@hotmail.com", "", "", "", "", "", "", "US", - ""); - - EXPECT_FALSE(profile1.IsSubsetOf(profile2, "en-US")); - EXPECT_TRUE(profile2.IsSubsetOf(profile1, "en-US")); - EXPECT_FALSE(profile2.IsSubsetOf(profile3, "en-US")); - EXPECT_FALSE(profile3.IsSubsetOf(profile2, "en-US")); -} - TEST(AutofillProfileTest, IsSubsetOfForFieldSet_DifferentMiddleNames) { AutofillProfile profile1 = AutofillProfile(base::GenerateGUID(), test::kEmptyOrigin); @@ -2210,7 +2186,7 @@ TEST_P(HasGreaterFrescocencyTest, HasGreaterFrescocency) { test_case.server_validity_state_b, AutofillDataModel::SERVER); - const base::Time now = base::Time::Now(); + const base::Time now = AutofillClock::Now(); if (test_case.expectation == EQUAL) { EXPECT_EQ(profile_a.HasGreaterFrecencyThan(&profile_b, now), @@ -2249,7 +2225,7 @@ TEST_P(HasGreaterFrescocencyTest, PriorityCheck) { profile_invalid.set_use_count(100); profile_valid.set_use_count(10); - const base::Time now = base::Time::Now(); + const base::Time now = AutofillClock::Now(); const base::Time past = now - base::TimeDelta::FromDays(1); profile_invalid.set_use_date(now); diff --git a/chromium/components/autofill/core/browser/data_model/contact_info.cc b/chromium/components/autofill/core/browser/data_model/contact_info.cc index 895e1ed0a52..668230e72f0 100644 --- a/chromium/components/autofill/core/browser/data_model/contact_info.cc +++ b/chromium/components/autofill/core/browser/data_model/contact_info.cc @@ -47,31 +47,6 @@ bool NameInfo::operator==(const NameInfo& other) const { family_ == other.family_ && full_ == other.full_; } -bool NameInfo::ParsedNamesAreEqual(const NameInfo& info) const { - return given_ == info.given_ && middle_ == info.middle_ && - family_ == info.family_; -} - -void NameInfo::OverwriteName(const NameInfo& new_name) { - if (!new_name.given_.empty()) - given_ = new_name.given_; - - // For the middle name, don't overwrite a full middle name with an initial. - if (!new_name.middle_.empty() && - (middle_.size() <= 1 || new_name.middle_.size() > 1)) - middle_ = new_name.middle_; - - if (!new_name.family_.empty()) - family_ = new_name.family_; - - if (!new_name.full_.empty()) - full_ = new_name.full_; -} - -bool NameInfo::NamePartsAreEmpty() const { - return given_.empty() && middle_.empty() && family_.empty(); -} - base::string16 NameInfo::GetRawInfo(ServerFieldType type) const { DCHECK_EQ(NAME, AutofillType(type).group()); switch (type) { @@ -250,15 +225,26 @@ void CompanyInfo::SetRawInfo(ServerFieldType type, } bool CompanyInfo::IsValidOrVerified(const base::string16& value) const { - if (!base::FeatureList::IsEnabled( - autofill::features::kAutofillRejectCompanyBirthyear)) - return true; - - if (profile_ && profile_->IsVerified()) + if (profile_ && profile_->IsVerified()) { return true; - - // Companies that are of the format of a four digit birth year are not valid. - return !MatchesPattern(value, base::UTF8ToUTF16("^(19|20)\\d{2}$")); + } + // |value| is a birthyear: + if (base::FeatureList::IsEnabled( + autofill::features::kAutofillRejectCompanyBirthyear) && + MatchesPattern(value, base::UTF8ToUTF16("^(19|20)\\d{2}$"))) { + return false; + } + // |value| is a social title: + if (base::FeatureList::IsEnabled( + autofill::features::kAutofillRejectCompanySocialTitle) && + MatchesPattern(value, base::UTF8ToUTF16( + "^(Ms\\.?|Mrs\\.?|Mr\\.?|Miss|Mistress|Mister|" + "Frau|Herr|" + "Mlle|Mme|M\\.|" + "Dr\\.?|Prof\\.?)$"))) { + return false; + } + return true; } } // namespace autofill diff --git a/chromium/components/autofill/core/browser/data_model/contact_info.h b/chromium/components/autofill/core/browser/data_model/contact_info.h index b838253f118..cedfe7bedcb 100644 --- a/chromium/components/autofill/core/browser/data_model/contact_info.h +++ b/chromium/components/autofill/core/browser/data_model/contact_info.h @@ -26,18 +26,6 @@ class NameInfo : public FormGroup { bool operator==(const NameInfo& other) const; bool operator!=(const NameInfo& other) const { return !operator==(other); } - // Compares |NameInfo| objects for |given_|, |middle_| and |family_| names. - // The comparison is case sensitive. - bool ParsedNamesAreEqual(const NameInfo& info) const; - - // For every non-empty NameInfo part in |new_name|, the corresponding NameInfo - // part in | this | is overwritten.Special logic so that a middle initial may - // not overwrite a full middle name. - void OverwriteName(const NameInfo& new_name); - - // Returns true if all the name parts (first, middle and last) are empty. - bool NamePartsAreEmpty() const; - // FormGroup: base::string16 GetRawInfo(ServerFieldType type) const override; void SetRawInfo(ServerFieldType type, const base::string16& value) override; diff --git a/chromium/components/autofill/core/browser/data_model/contact_info_unittest.cc b/chromium/components/autofill/core/browser/data_model/contact_info_unittest.cc index a5e64b41128..e0bcfceb9d8 100644 --- a/chromium/components/autofill/core/browser/data_model/contact_info_unittest.cc +++ b/chromium/components/autofill/core/browser/data_model/contact_info_unittest.cc @@ -183,223 +183,119 @@ TEST(NameInfoTest, GetFullName) { name.GetInfo(AutofillType(NAME_FULL), "en-US")); } -struct ParsedNamesAreEqualTestCase { - std::string starting_names[3]; - std::string additional_names[3]; - bool expected_result; -}; +TEST(CompanyTest, CompanyNameYear) { + base::test::ScopedFeatureList scoped_features; + scoped_features.InitWithFeatures( + /*enabled_features=*/{features::kAutofillRejectCompanyBirthyear}, + /*disabled_features=*/{}); -class ParsedNamesAreEqualTest - : public testing::TestWithParam<ParsedNamesAreEqualTestCase> {}; + AutofillProfile profile; + CompanyInfo company(&profile); + ASSERT_FALSE(profile.IsVerified()); -TEST_P(ParsedNamesAreEqualTest, ParsedNamesAreEqual) { - auto test_case = GetParam(); + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Google")); + EXPECT_EQ(UTF8ToUTF16("Google"), company.GetRawInfo(COMPANY_NAME)); - // Construct the starting_profile. - NameInfo starting_profile; - starting_profile.SetRawInfo(NAME_FIRST, - UTF8ToUTF16(test_case.starting_names[0])); - starting_profile.SetRawInfo(NAME_MIDDLE, - UTF8ToUTF16(test_case.starting_names[1])); - starting_profile.SetRawInfo(NAME_LAST, - UTF8ToUTF16(test_case.starting_names[2])); - - // Construct the additional_profile. - NameInfo additional_profile; - additional_profile.SetRawInfo(NAME_FIRST, - UTF8ToUTF16(test_case.additional_names[0])); - additional_profile.SetRawInfo(NAME_MIDDLE, - UTF8ToUTF16(test_case.additional_names[1])); - additional_profile.SetRawInfo(NAME_LAST, - UTF8ToUTF16(test_case.additional_names[2])); - - // Verify the test expectations. - EXPECT_EQ(test_case.expected_result, - starting_profile.ParsedNamesAreEqual(additional_profile)); -} + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("1987")); + EXPECT_EQ(UTF8ToUTF16(""), company.GetRawInfo(COMPANY_NAME)); -INSTANTIATE_TEST_SUITE_P( - ContactInfoTest, - ParsedNamesAreEqualTest, - testing::Values( - // Identical name comparison. - ParsedNamesAreEqualTestCase{{"Marion", "Mitchell", "Morrison"}, - {"Marion", "Mitchell", "Morrison"}, - true}, - - // Case-sensitive comparisons. - ParsedNamesAreEqualTestCase{{"Marion", "Mitchell", "Morrison"}, - {"Marion", "Mitchell", "MORRISON"}, - false}, - ParsedNamesAreEqualTestCase{{"Marion", "Mitchell", "Morrison"}, - {"MARION", "Mitchell", "MORRISON"}, - false}, - ParsedNamesAreEqualTestCase{{"Marion", "Mitchell", "Morrison"}, - {"MARION", "MITCHELL", "MORRISON"}, - false}, - ParsedNamesAreEqualTestCase{{"Marion", "", "Mitchell Morrison"}, - {"MARION", "", "MITCHELL MORRISON"}, - false}, - ParsedNamesAreEqualTestCase{{"Marion Mitchell", "", "Morrison"}, - {"MARION MITCHELL", "", "MORRISON"}, - false}, - - // Identical full names but different canonical forms. - ParsedNamesAreEqualTestCase{{"Marion", "Mitchell", "Morrison"}, - {"Marion", "", "Mitchell Morrison"}, - false}, - ParsedNamesAreEqualTestCase{{"Marion", "Mitchell", "Morrison"}, - {"Marion Mitchell", "", "MORRISON"}, - false}, - - // Different names. - ParsedNamesAreEqualTestCase{{"Marion", "Mitchell", "Morrison"}, - {"Marion", "M.", "Morrison"}, - false}, - ParsedNamesAreEqualTestCase{{"Marion", "Mitchell", "Morrison"}, - {"MARION", "M.", "MORRISON"}, - false}, - ParsedNamesAreEqualTestCase{{"Marion", "Mitchell", "Morrison"}, - {"David", "Mitchell", "Morrison"}, - false}, - - // Non-ASCII characters. - ParsedNamesAreEqualTestCase{{"M\xc3\xa1rion Mitchell", "", "Morrison"}, - {"M\xc3\xa1rion Mitchell", "", "Morrison"}, - true})); - -struct OverwriteNameTestCase { - std::string existing_name[4]; - std::string new_name[4]; - std::string expected_name[4]; -}; + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("It was 1987.")); + EXPECT_EQ(UTF8ToUTF16("It was 1987."), company.GetRawInfo(COMPANY_NAME)); -class OverwriteNameTest : public testing::TestWithParam<OverwriteNameTestCase> { -}; + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("1987 was the year.")); + EXPECT_EQ(UTF8ToUTF16("1987 was the year."), + company.GetRawInfo(COMPANY_NAME)); -TEST_P(OverwriteNameTest, OverwriteName) { - auto test_case = GetParam(); - // Construct the starting_profile. - NameInfo existing_name; - existing_name.SetRawInfo(NAME_FIRST, UTF8ToUTF16(test_case.existing_name[0])); - existing_name.SetRawInfo(NAME_MIDDLE, - UTF8ToUTF16(test_case.existing_name[1])); - existing_name.SetRawInfo(NAME_LAST, UTF8ToUTF16(test_case.existing_name[2])); - existing_name.SetRawInfo(NAME_FULL, UTF8ToUTF16(test_case.existing_name[3])); - - // Construct the additional_profile. - NameInfo new_name; - new_name.SetRawInfo(NAME_FIRST, UTF8ToUTF16(test_case.new_name[0])); - new_name.SetRawInfo(NAME_MIDDLE, UTF8ToUTF16(test_case.new_name[1])); - new_name.SetRawInfo(NAME_LAST, UTF8ToUTF16(test_case.new_name[2])); - new_name.SetRawInfo(NAME_FULL, UTF8ToUTF16(test_case.new_name[3])); - - existing_name.OverwriteName(new_name); - - // Verify the test expectations. - EXPECT_EQ(UTF8ToUTF16(test_case.expected_name[0]), - existing_name.GetRawInfo(NAME_FIRST)); - EXPECT_EQ(UTF8ToUTF16(test_case.expected_name[1]), - existing_name.GetRawInfo(NAME_MIDDLE)); - EXPECT_EQ(UTF8ToUTF16(test_case.expected_name[2]), - existing_name.GetRawInfo(NAME_LAST)); - EXPECT_EQ(UTF8ToUTF16(test_case.expected_name[3]), - existing_name.GetRawInfo(NAME_FULL)); -} + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Yes, 1987 was the year.")); + EXPECT_EQ(UTF8ToUTF16("Yes, 1987 was the year."), + company.GetRawInfo(COMPANY_NAME)); -INSTANTIATE_TEST_SUITE_P( - ContactInfoTest, - OverwriteNameTest, - testing::Values( - // Missing information in the original name gets filled with the new - // name's information. - OverwriteNameTestCase{ - {"", "", "", ""}, - {"Marion", "Mitchell", "Morrison", "Marion Mitchell Morrison"}, - {"Marion", "Mitchell", "Morrison", "Marion Mitchell Morrison"}, - }, - // The new name's values overwrite the exsiting name values if they are - // different - OverwriteNameTestCase{ - {"Marion", "Mitchell", "Morrison", "Marion Mitchell Morrison"}, - {"Mario", "Mitchell", "Thompson", "Mario Mitchell Morrison"}, - {"Mario", "Mitchell", "Thompson", "Mario Mitchell Morrison"}, - }, - // An existing name values do not get replaced with empty values. - OverwriteNameTestCase{ - {"Marion", "Mitchell", "Morrison", "Marion Mitchell Morrison"}, - {"", "", "", ""}, - {"Marion", "Mitchell", "Morrison", "Marion Mitchell Morrison"}, - }, - // An existing full middle not does not get replaced by a middle name - // initial. - OverwriteNameTestCase{ - {"Marion", "Mitchell", "Morrison", "Marion Mitchell Morrison"}, - {"Marion", "M", "Morrison", "Marion Mitchell Morrison"}, - {"Marion", "Mitchell", "Morrison", "Marion Mitchell Morrison"}, - }, - // An existing middle name initial is overwritten by the new profile's - // middle name value. - OverwriteNameTestCase{ - {"Marion", "M", "Morrison", "Marion Mitchell Morrison"}, - {"Marion", "Mitchell", "Morrison", "Marion Mitchell Morrison"}, - {"Marion", "Mitchell", "Morrison", "Marion Mitchell Morrison"}, - }, - // A NameInfo with only the full name set overwritten with a NameInfo - // with only the name parts set result in a NameInfo with all the name - // parts and name full set. - OverwriteNameTestCase{ - {"", "", "", "Marion Mitchell Morrison"}, - {"Marion", "Mitchell", "Morrison", ""}, - {"Marion", "Mitchell", "Morrison", "Marion Mitchell Morrison"}, - }, - // A NameInfo with only the name parts set overwritten with a NameInfo - // with only the full name set result in a NameInfo with all the name - // parts and name full set. - OverwriteNameTestCase{ - {"Marion", "Mitchell", "Morrison", ""}, - {"", "", "", "Marion Mitchell Morrison"}, - {"Marion", "Mitchell", "Morrison", "Marion Mitchell Morrison"}, - })); - -struct NamePartsAreEmptyTestCase { - std::string first; - std::string middle; - std::string last; - std::string full; - bool expected_result; -}; + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("2019")); + EXPECT_EQ(UTF8ToUTF16(""), company.GetRawInfo(COMPANY_NAME)); -class NamePartsAreEmptyTest - : public testing::TestWithParam<NamePartsAreEmptyTestCase> {}; + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("1818")); + EXPECT_EQ(UTF8ToUTF16("1818"), company.GetRawInfo(COMPANY_NAME)); -TEST_P(NamePartsAreEmptyTest, NamePartsAreEmpty) { - auto test_case = GetParam(); - // Construct the NameInfo. - NameInfo name; - name.SetRawInfo(NAME_FIRST, UTF8ToUTF16(test_case.first)); - name.SetRawInfo(NAME_MIDDLE, UTF8ToUTF16(test_case.middle)); - name.SetRawInfo(NAME_LAST, UTF8ToUTF16(test_case.last)); - name.SetRawInfo(NAME_FULL, UTF8ToUTF16(test_case.full)); + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("2345")); + EXPECT_EQ(UTF8ToUTF16("2345"), company.GetRawInfo(COMPANY_NAME)); - // Verify the test expectations. - EXPECT_EQ(test_case.expected_result, name.NamePartsAreEmpty()); -} + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Mr")); + EXPECT_EQ(UTF8ToUTF16("Mr"), company.GetRawInfo(COMPANY_NAME)); -INSTANTIATE_TEST_SUITE_P( - ContactInfoTest, - NamePartsAreEmptyTest, - testing::Values(NamePartsAreEmptyTestCase{"", "", "", "", true}, - NamePartsAreEmptyTestCase{"", "", "", - "Marion Mitchell Morrison", true}, - NamePartsAreEmptyTestCase{"Marion", "", "", "", false}, - NamePartsAreEmptyTestCase{"", "Mitchell", "", "", false}, - NamePartsAreEmptyTestCase{"", "", "Morrison", "", false})); + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Mr.")); + EXPECT_EQ(UTF8ToUTF16("Mr."), company.GetRawInfo(COMPANY_NAME)); -TEST(CompanyTest, CompanyNameYear) { + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Mrs")); + EXPECT_EQ(UTF8ToUTF16("Mrs"), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Mrs.")); + EXPECT_EQ(UTF8ToUTF16("Mrs."), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Mr. & Mrs.")); + EXPECT_EQ(UTF8ToUTF16("Mr. & Mrs."), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Mr. & Mrs. Smith")); + EXPECT_EQ(UTF8ToUTF16("Mr. & Mrs. Smith"), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Frau")); + EXPECT_EQ(UTF8ToUTF16("Frau"), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Frau Doktor")); + EXPECT_EQ(UTF8ToUTF16("Frau Doktor"), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Herr")); + EXPECT_EQ(UTF8ToUTF16("Herr"), company.GetRawInfo(COMPANY_NAME)); + + profile.set_origin("Not empty"); + ASSERT_TRUE(profile.IsVerified()); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Google")); + EXPECT_EQ(UTF8ToUTF16("Google"), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("1987")); + EXPECT_EQ(UTF8ToUTF16("1987"), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("2019")); + EXPECT_EQ(UTF8ToUTF16("2019"), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("1818")); + EXPECT_EQ(UTF8ToUTF16("1818"), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("2345")); + EXPECT_EQ(UTF8ToUTF16("2345"), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Mr")); + EXPECT_EQ(UTF8ToUTF16("Mr"), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Mr.")); + EXPECT_EQ(UTF8ToUTF16("Mr."), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Mrs")); + EXPECT_EQ(UTF8ToUTF16("Mrs"), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Mrs.")); + EXPECT_EQ(UTF8ToUTF16("Mrs."), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Mr. & Mrs.")); + EXPECT_EQ(UTF8ToUTF16("Mr. & Mrs."), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Mr. & Mrs. Smith")); + EXPECT_EQ(UTF8ToUTF16("Mr. & Mrs. Smith"), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Frau")); + EXPECT_EQ(UTF8ToUTF16("Frau"), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Frau Doktor")); + EXPECT_EQ(UTF8ToUTF16("Frau Doktor"), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Herr")); + EXPECT_EQ(UTF8ToUTF16("Herr"), company.GetRawInfo(COMPANY_NAME)); +} + +TEST(CompanyTest, CompanyNameSocialTitle) { base::test::ScopedFeatureList scoped_features; scoped_features.InitWithFeatures( - /*enabled_features=*/{features::kAutofillRejectCompanyBirthyear}, + /*enabled_features=*/{features::kAutofillRejectCompanySocialTitle}, /*disabled_features=*/{}); AutofillProfile profile; @@ -410,7 +306,7 @@ TEST(CompanyTest, CompanyNameYear) { EXPECT_EQ(UTF8ToUTF16("Google"), company.GetRawInfo(COMPANY_NAME)); company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("1987")); - EXPECT_EQ(UTF8ToUTF16(""), company.GetRawInfo(COMPANY_NAME)); + EXPECT_EQ(UTF8ToUTF16("1987"), company.GetRawInfo(COMPANY_NAME)); company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("It was 1987.")); EXPECT_EQ(UTF8ToUTF16("It was 1987."), company.GetRawInfo(COMPANY_NAME)); @@ -424,7 +320,7 @@ TEST(CompanyTest, CompanyNameYear) { company.GetRawInfo(COMPANY_NAME)); company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("2019")); - EXPECT_EQ(UTF8ToUTF16(""), company.GetRawInfo(COMPANY_NAME)); + EXPECT_EQ(UTF8ToUTF16("2019"), company.GetRawInfo(COMPANY_NAME)); company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("1818")); EXPECT_EQ(UTF8ToUTF16("1818"), company.GetRawInfo(COMPANY_NAME)); @@ -432,6 +328,33 @@ TEST(CompanyTest, CompanyNameYear) { company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("2345")); EXPECT_EQ(UTF8ToUTF16("2345"), company.GetRawInfo(COMPANY_NAME)); + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Mr")); + EXPECT_EQ(UTF8ToUTF16(""), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Mr.")); + EXPECT_EQ(UTF8ToUTF16(""), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Mrs")); + EXPECT_EQ(UTF8ToUTF16(""), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Mrs.")); + EXPECT_EQ(UTF8ToUTF16(""), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Mr. & Mrs.")); + EXPECT_EQ(UTF8ToUTF16("Mr. & Mrs."), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Mr. & Mrs. Smith")); + EXPECT_EQ(UTF8ToUTF16("Mr. & Mrs. Smith"), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Frau")); + EXPECT_EQ(UTF8ToUTF16(""), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Frau Doktor")); + EXPECT_EQ(UTF8ToUTF16("Frau Doktor"), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Herr")); + EXPECT_EQ(UTF8ToUTF16(""), company.GetRawInfo(COMPANY_NAME)); + profile.set_origin("Not empty"); ASSERT_TRUE(profile.IsVerified()); @@ -449,6 +372,33 @@ TEST(CompanyTest, CompanyNameYear) { company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("2345")); EXPECT_EQ(UTF8ToUTF16("2345"), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Mr")); + EXPECT_EQ(UTF8ToUTF16("Mr"), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Mr.")); + EXPECT_EQ(UTF8ToUTF16("Mr."), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Mrs")); + EXPECT_EQ(UTF8ToUTF16("Mrs"), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Mrs.")); + EXPECT_EQ(UTF8ToUTF16("Mrs."), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Mr. & Mrs.")); + EXPECT_EQ(UTF8ToUTF16("Mr. & Mrs."), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Mr. & Mrs. Smith")); + EXPECT_EQ(UTF8ToUTF16("Mr. & Mrs. Smith"), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Frau")); + EXPECT_EQ(UTF8ToUTF16("Frau"), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Frau Doktor")); + EXPECT_EQ(UTF8ToUTF16("Frau Doktor"), company.GetRawInfo(COMPANY_NAME)); + + company.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Herr")); + EXPECT_EQ(UTF8ToUTF16("Herr"), company.GetRawInfo(COMPANY_NAME)); } TEST(CompanyTest, CompanyNameYearCopy) { @@ -488,4 +438,41 @@ TEST(CompanyTest, CompanyNameYearIsEqual) { EXPECT_EQ(company_old, company_young); } +TEST(CompanyTest, CompanyNameSocialTitleCopy) { + base::test::ScopedFeatureList scoped_features; + scoped_features.InitWithFeatures( + /*enabled_features=*/{features::kAutofillRejectCompanySocialTitle}, + /*disabled_features=*/{}); + + AutofillProfile profile; + ASSERT_FALSE(profile.IsVerified()); + + CompanyInfo company_google(&profile); + CompanyInfo company_year(&profile); + + company_google.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Google")); + company_year.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Prof.")); + + company_google = company_year; + EXPECT_EQ(UTF8ToUTF16(""), company_google.GetRawInfo(COMPANY_NAME)); +} + +TEST(CompanyTest, CompanyNameSocialTitleIsEqual) { + base::test::ScopedFeatureList scoped_features; + scoped_features.InitWithFeatures( + /*enabled_features=*/{features::kAutofillRejectCompanySocialTitle}, + /*disabled_features=*/{}); + + AutofillProfile profile; + ASSERT_FALSE(profile.IsVerified()); + + CompanyInfo company_old(&profile); + CompanyInfo company_young(&profile); + + company_old.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Dr")); + company_young.SetRawInfo(COMPANY_NAME, UTF8ToUTF16("Prof")); + + EXPECT_EQ(company_old, company_young); +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/data_model/credit_card.cc b/chromium/components/autofill/core/browser/data_model/credit_card.cc index 4235a1b05a7..32c713d40c8 100644 --- a/chromium/components/autofill/core/browser/data_model/credit_card.cc +++ b/chromium/components/autofill/core/browser/data_model/credit_card.cc @@ -539,6 +539,7 @@ void CreditCard::operator=(const CreditCard& credit_card) { bank_name_ = credit_card.bank_name_; temp_card_first_name_ = credit_card.temp_card_first_name_; temp_card_last_name_ = credit_card.temp_card_last_name_; + cloud_token_data_ = credit_card.cloud_token_data_; set_guid(credit_card.guid()); set_origin(credit_card.origin()); @@ -707,6 +708,11 @@ bool CreditCard::HasValidCardNumber() const { return IsValidCreditCardNumber(number_); } +bool CreditCard::HasValidExpirationYear() const { + return IsValidCreditCardExpirationYear(expiration_year_, + AutofillClock::Now()); +} + bool CreditCard::HasValidExpirationDate() const { return IsValidCreditCardExpirationDate(expiration_year_, expiration_month_, AutofillClock::Now()); diff --git a/chromium/components/autofill/core/browser/data_model/credit_card.h b/chromium/components/autofill/core/browser/data_model/credit_card.h index 0a4c3d65e27..bb074847459 100644 --- a/chromium/components/autofill/core/browser/data_model/credit_card.h +++ b/chromium/components/autofill/core/browser/data_model/credit_card.h @@ -16,6 +16,7 @@ #include "base/strings/string_piece_forward.h" #include "build/build_config.h" #include "components/autofill/core/browser/data_model/autofill_data_model.h" +#include "components/sync/protocol/sync.pb.h" namespace autofill { @@ -142,6 +143,13 @@ class CreditCard : public AutofillDataModel { const std::string& server_id() const { return server_id_; } void set_server_id(const std::string& server_id) { server_id_ = server_id; } + const sync_pb::CloudTokenData& cloud_token_data() const { + return cloud_token_data_; + } + void set_cloud_token_data(const sync_pb::CloudTokenData& cloud_token_data) { + cloud_token_data_ = cloud_token_data; + } + // For use in STL containers. void operator=(const CreditCard& credit_card); @@ -190,6 +198,9 @@ class CreditCard : public AutofillDataModel { // not complete. bool HasValidCardNumber() const; + // Returns true if credit card has valid expiration year. + bool HasValidExpirationYear() const; + // Returns true if credit card has valid expiration date. bool HasValidExpirationDate() const; @@ -351,6 +362,9 @@ class CreditCard : public AutofillDataModel { // since we only store the full name. base::string16 temp_card_first_name_; base::string16 temp_card_last_name_; + + // Info of tokenizized credit card if available. + sync_pb::CloudTokenData cloud_token_data_; }; // So we can compare CreditCards with EXPECT_EQ(). diff --git a/chromium/components/autofill/core/browser/data_model/credit_card_unittest.cc b/chromium/components/autofill/core/browser/data_model/credit_card_unittest.cc index beda49cdaf0..c420cd5dc54 100644 --- a/chromium/components/autofill/core/browser/data_model/credit_card_unittest.cc +++ b/chromium/components/autofill/core/browser/data_model/credit_card_unittest.cc @@ -60,7 +60,7 @@ const char* const kInvalidNumbers[] = { // Use this function to generate a year in the future. base::string16 GetYearInTheFuture() { base::Time::Exploded now; - base::Time::Now().LocalExplode(&now); + AutofillClock::Now().LocalExplode(&now); return base::NumberToString16(now.year + 4); } @@ -1023,7 +1023,7 @@ TEST(CreditCardTest, TEST(CreditCardTest, IsValidCardNumberAndExpiryDate) { CreditCard card; // Invalid because expired - const base::Time now(base::Time::Now()); + const base::Time now(AutofillClock::Now()); base::Time::Exploded now_exploded; now.LocalExplode(&now_exploded); card.SetRawInfo(CREDIT_CARD_EXP_MONTH, @@ -1547,7 +1547,7 @@ class ShouldUpdateExpirationTest class TestingTimes { public: TestingTimes() { - now_ = base::Time::Now(); + now_ = AutofillClock::Now(); (now_ - base::TimeDelta::FromDays(365)).LocalExplode(&last_year_); (now_ - base::TimeDelta::FromDays(31)).LocalExplode(&last_month_); now_.LocalExplode(¤t_); diff --git a/chromium/components/autofill/core/browser/field_types.h b/chromium/components/autofill/core/browser/field_types.h index 3080ba16f68..e874de0c453 100644 --- a/chromium/components/autofill/core/browser/field_types.h +++ b/chromium/components/autofill/core/browser/field_types.h @@ -189,9 +189,26 @@ enum ServerFieldType { // https://en.wikipedia.org/wiki/Unified_Payments_Interface UPI_VPA = 102, + // Just the street name of an address, no house number. + // Currently not used by Chrome. + ADDRESS_HOME_STREET = 103, + + // House number of an address, may be alphanumeric. + // Currently not used by Chrome. + ADDRESS_HOME_HOUSE_NUMBER = 104, + + // Floor within in a building, may be alphanumeric. + // Currently not used by Chrome. + ADDRESS_HOME_FLOOR = 105, + + // A catch-all for other type of subunits (only used until something more + // precise is defined). + // Currently not used by Chrome. + ADDRESS_HOME_OTHER_SUBUNIT = 106, + // No new types can be added without a corresponding change to the Autofill // server. - MAX_VALID_FIELD_TYPE = 103, + MAX_VALID_FIELD_TYPE = 107, }; // The list of all HTML autocomplete field type hints supported by Chrome. diff --git a/chromium/components/autofill/core/browser/form_data_importer.cc b/chromium/components/autofill/core/browser/form_data_importer.cc index 5851f7d6358..4323f547479 100644 --- a/chromium/components/autofill/core/browser/form_data_importer.cc +++ b/chromium/components/autofill/core/browser/form_data_importer.cc @@ -103,6 +103,8 @@ FormDataImporter::FormDataImporter(AutofillClient* client, payments_client, app_locale, personal_data_manager)), + upi_vpa_save_manager_( + std::make_unique<UpiVpaSaveManager>(personal_data_manager)), local_card_migration_manager_( std::make_unique<LocalCardMigrationManager>(client, payments_client, @@ -118,6 +120,7 @@ void FormDataImporter::ImportFormData(const FormStructure& submitted_form, bool profile_autofill_enabled, bool credit_card_autofill_enabled) { std::unique_ptr<CreditCard> imported_credit_card; + base::Optional<std::string> detected_vpa; bool is_credit_card_upstream_enabled = credit_card_save_manager_->IsCreditCardUploadEnabled(); @@ -128,7 +131,13 @@ void FormDataImporter::ImportFormData(const FormStructure& submitted_form, ImportFormData(submitted_form, profile_autofill_enabled, credit_card_autofill_enabled, /*should_return_local_card=*/is_credit_card_upstream_enabled, - &imported_credit_card); + &imported_credit_card, &detected_vpa); + + if (detected_vpa && credit_card_autofill_enabled && + base::FeatureList::IsEnabled(features::kAutofillSaveAndFillVPA)) { + upi_vpa_save_manager_->OfferLocalSave(*detected_vpa); + } + // If no card was successfully imported from the form, return. if (imported_credit_card_record_type_ == ImportedCreditCardRecordType::NO_CARD) { @@ -214,7 +223,8 @@ bool FormDataImporter::ImportFormData( bool profile_autofill_enabled, bool credit_card_autofill_enabled, bool should_return_local_card, - std::unique_ptr<CreditCard>* imported_credit_card) { + std::unique_ptr<CreditCard>* imported_credit_card, + base::Optional<std::string>* imported_vpa) { // We try the same |form| for both credit card and address import/update. // - ImportCreditCard may update an existing card, or fill // |imported_credit_card| with an extracted card. See .h for details of @@ -226,6 +236,7 @@ bool FormDataImporter::ImportFormData( if (credit_card_autofill_enabled) { cc_import = ImportCreditCard(submitted_form, should_return_local_card, imported_credit_card); + *imported_vpa = ImportVPA(submitted_form); } // - ImportAddressProfiles may eventually save or update one or more address // profiles. @@ -233,7 +244,8 @@ bool FormDataImporter::ImportFormData( if (profile_autofill_enabled) { address_import = ImportAddressProfiles(submitted_form); } - if (cc_import || address_import) + + if (cc_import || address_import || imported_vpa->has_value()) return true; personal_data_manager_->MarkObserversInsufficientFormDataForImport(); @@ -535,4 +547,13 @@ CreditCard FormDataImporter::ExtractCreditCardFromForm( return candidate_credit_card; } +base::Optional<std::string> FormDataImporter::ImportVPA( + const FormStructure& form) { + for (const auto& field : form) { + if (IsUPIVirtualPaymentAddress(field->value)) + return base::UTF16ToUTF8(field->value); + } + return base::nullopt; +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/form_data_importer.h b/chromium/components/autofill/core/browser/form_data_importer.h index 1fd598e0ba2..e81346a28bf 100644 --- a/chromium/components/autofill/core/browser/form_data_importer.h +++ b/chromium/components/autofill/core/browser/form_data_importer.h @@ -17,8 +17,11 @@ #include "components/autofill/core/browser/payments/credit_card_save_manager.h" #include "components/autofill/core/browser/payments/local_card_migration_manager.h" #include "components/autofill/core/browser/payments/payments_client.h" +#include "components/autofill/core/browser/payments/upi_vpa_save_manager.h" #include "components/autofill/core/browser/personal_data_manager.h" +class SaveCardOfferObserver; + namespace autofill { // Manages logic for importing address profiles and credit card information from @@ -87,13 +90,16 @@ class FormDataImporter { // data. If the form contains credit card data already present in a local // credit card entry *and* |should_return_local_card| is true, the data is // stored into |imported_credit_card| so that we can prompt the user whether - // to upload it. Returns |true| if sufficient address or credit card data + // to upload it. If the form contains UPI/VPA data and + // |credit_card_autofill_enabled| is true, the VPA value will be stored into + // |imported_vpa|. Returns |true| if sufficient address or credit card data // was found. Exposed for testing. bool ImportFormData(const FormStructure& form, bool profile_autofill_enabled, bool credit_card_autofill_enabled, bool should_return_local_card, - std::unique_ptr<CreditCard>* imported_credit_card); + std::unique_ptr<CreditCard>* imported_credit_card, + base::Optional<std::string>* imported_vpa); // Go through the |form| fields and attempt to extract and import valid // address profiles. Returns true on extraction success of at least one @@ -120,6 +126,10 @@ class FormDataImporter { CreditCard ExtractCreditCardFromForm(const FormStructure& form, bool* hasDuplicateFieldType); + // Go through the |form| fields and find a UPI/VPA value to import. The return + // value will be empty if no VPA was found. + base::Optional<std::string> ImportVPA(const FormStructure& form); + // Whether a dynamic change form is imported. bool from_dynamic_change_form_ = false; @@ -133,6 +143,9 @@ class FormDataImporter { // Responsible for managing credit card save flows (local or upload). std::unique_ptr<CreditCardSaveManager> credit_card_save_manager_; + // Responsible for managing UPI/VPA save flows. + std::unique_ptr<UpiVpaSaveManager> upi_vpa_save_manager_; + // Responsible for migrating locally saved credit cards to Google Pay. std::unique_ptr<LocalCardMigrationManager> local_card_migration_manager_; @@ -155,6 +168,7 @@ class FormDataImporter { friend class LocalCardMigrationBrowserTest; friend class SaveCardBubbleViewsFullFormBrowserTest; friend class SaveCardInfobarEGTestHelper; + friend class ::SaveCardOfferObserver; FRIEND_TEST_ALL_PREFIXES(AutofillMergeTest, MergeProfiles); FRIEND_TEST_ALL_PREFIXES(FormDataImporterTest, AllowDuplicateMaskedServerCardIfFlagEnabled); @@ -197,6 +211,8 @@ class FormDataImporter { ImportFormData_SecondImportResetsCreditCardRecordType); FRIEND_TEST_ALL_PREFIXES(FormDataImporterTest, ImportFormData_TwoAddressesOneCreditCard); + FRIEND_TEST_ALL_PREFIXES(FormDataImporterTest, + ImportFormData_DontSetVPAWhenOnlyCreditCardExists); FRIEND_TEST_ALL_PREFIXES( FormDataImporterTest, Metrics_SubmittedServerCardExpirationStatus_FullServerCardMatch); @@ -218,6 +234,9 @@ class FormDataImporter { FRIEND_TEST_ALL_PREFIXES( FormDataImporterTest, Metrics_SubmittedDifferentServerCardExpirationStatus_EmptyExpirationYear); + FRIEND_TEST_ALL_PREFIXES(FormDataImporterTest, ImportVPA); + FRIEND_TEST_ALL_PREFIXES(FormDataImporterTest, ImportVPADisabled); + FRIEND_TEST_ALL_PREFIXES(FormDataImporterTest, ImportVPAIgnoreNonVPA); DISALLOW_COPY_AND_ASSIGN(FormDataImporter); }; diff --git a/chromium/components/autofill/core/browser/form_data_importer_unittest.cc b/chromium/components/autofill/core/browser/form_data_importer_unittest.cc index 703f2ae0d00..55ec7e1b5b8 100644 --- a/chromium/components/autofill/core/browser/form_data_importer_unittest.cc +++ b/chromium/components/autofill/core/browser/form_data_importer_unittest.cc @@ -223,8 +223,8 @@ class FormDataImporterTestBase { run_loop.Run(); } - base::test::TaskEnvironment task_environment_{ - base::test::TaskEnvironment::MainThreadType::UI}; + base::test::SingleThreadTaskEnvironment task_environment_{ + base::test::SingleThreadTaskEnvironment::MainThreadType::UI}; std::unique_ptr<PrefService> prefs_; scoped_refptr<AutofillWebDataService> autofill_database_service_; scoped_refptr<WebDatabaseService> web_database_; @@ -2138,10 +2138,11 @@ TEST_F(FormDataImporterTest, FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); std::unique_ptr<CreditCard> imported_credit_card; + base::Optional<std::string> imported_vpa; EXPECT_TRUE(form_data_importer_->ImportFormData( form_structure, /*profile_autofill_enabled=*/true, /*credit_card_autofill_enabled=*/true, - /*should_return_local_card=*/true, &imported_credit_card)); + /*should_return_local_card=*/true, &imported_credit_card, &imported_vpa)); ASSERT_TRUE(imported_credit_card); // |imported_credit_card_record_type_| should be LOCAL_CARD because upload was // offered and the card is a local card already on the device. @@ -2163,7 +2164,8 @@ TEST_F(FormDataImporterTest, EXPECT_TRUE(form_data_importer_->ImportFormData( form_structure2, /*profile_autofill_enabled=*/true, /*credit_card_autofill_enabled=*/true, - /*should_return_local_card=*/true, &imported_credit_card2)); + /*should_return_local_card=*/true, &imported_credit_card2, + &imported_vpa)); ASSERT_TRUE(imported_credit_card2); // |imported_credit_card_record_type_| should be NEW_CARD because the imported // card is not already on the device. @@ -2202,7 +2204,8 @@ TEST_F(FormDataImporterTest, EXPECT_TRUE(form_data_importer_->ImportFormData( form_structure3, /*profile_autofill_enabled=*/true, /*credit_card_autofill_enabled=*/false, - /*should_return_local_card=*/true, &imported_credit_card3)); + /*should_return_local_card=*/true, &imported_credit_card3, + &imported_vpa)); // |imported_credit_card_record_type_| should be NO_CARD because no valid card // was imported from the form. ASSERT_TRUE(form_data_importer_->imported_credit_card_record_type_ == @@ -2222,10 +2225,11 @@ TEST_F(FormDataImporterTest, FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); std::unique_ptr<CreditCard> imported_credit_card; + base::Optional<std::string> imported_vpa; EXPECT_TRUE(form_data_importer_->ImportFormData( form_structure, /*profile_autofill_enabled=*/true, /*credit_card_autofill_enabled=*/true, - /*should_return_local_card=*/true, &imported_credit_card)); + /*should_return_local_card=*/true, &imported_credit_card, &imported_vpa)); ASSERT_TRUE(imported_credit_card); // |imported_credit_card_record_type_| should be NEW_CARD because the imported // card is not already on the device. @@ -2259,10 +2263,11 @@ TEST_F(FormDataImporterTest, FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); std::unique_ptr<CreditCard> imported_credit_card; + base::Optional<std::string> imported_vpa; EXPECT_TRUE(form_data_importer_->ImportFormData( form_structure, /*profile_autofill_enabled=*/true, /*credit_card_autofill_enabled=*/true, - /*should_return_local_card=*/true, &imported_credit_card)); + /*should_return_local_card=*/true, &imported_credit_card, &imported_vpa)); ASSERT_TRUE(imported_credit_card); // |imported_credit_card_record_type_| should be LOCAL_CARD because upload was // offered and the card is a local card already on the device. @@ -2296,10 +2301,11 @@ TEST_F(FormDataImporterTest, FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); std::unique_ptr<CreditCard> imported_credit_card; + base::Optional<std::string> imported_vpa; EXPECT_FALSE(form_data_importer_->ImportFormData( form_structure, /*profile_autofill_enabled=*/true, /*credit_card_autofill_enabled=*/true, - /*should_return_local_card=*/true, &imported_credit_card)); + /*should_return_local_card=*/true, &imported_credit_card, &imported_vpa)); ASSERT_FALSE(imported_credit_card); // |imported_credit_card_record_type_| should be SERVER_CARD. ASSERT_TRUE(form_data_importer_->imported_credit_card_record_type_ == @@ -2332,10 +2338,11 @@ TEST_F(FormDataImporterTest, FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); std::unique_ptr<CreditCard> imported_credit_card; + base::Optional<std::string> imported_vpa; EXPECT_FALSE(form_data_importer_->ImportFormData( form_structure, /*profile_autofill_enabled=*/true, /*credit_card_autofill_enabled=*/true, - /*should_return_local_card=*/true, &imported_credit_card)); + /*should_return_local_card=*/true, &imported_credit_card, &imported_vpa)); ASSERT_FALSE(imported_credit_card); // |imported_credit_card_record_type_| should be SERVER_CARD. ASSERT_TRUE(form_data_importer_->imported_credit_card_record_type_ == @@ -2355,10 +2362,11 @@ TEST_F(FormDataImporterTest, FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); std::unique_ptr<CreditCard> imported_credit_card; + base::Optional<std::string> imported_vpa; EXPECT_FALSE(form_data_importer_->ImportFormData( form_structure, /*profile_autofill_enabled=*/true, /*credit_card_autofill_enabled=*/true, - /*should_return_local_card=*/true, &imported_credit_card)); + /*should_return_local_card=*/true, &imported_credit_card, &imported_vpa)); ASSERT_FALSE(imported_credit_card); // |imported_credit_card_record_type_| should be NO_CARD because no valid card // was successfully imported from the form. @@ -2382,10 +2390,11 @@ TEST_F( FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); std::unique_ptr<CreditCard> imported_credit_card; + base::Optional<std::string> imported_vpa; EXPECT_FALSE(form_data_importer_->ImportFormData( form_structure, /*profile_autofill_enabled=*/true, /*credit_card_autofill_enabled=*/true, - /*should_return_local_card=*/true, &imported_credit_card)); + /*should_return_local_card=*/true, &imported_credit_card, &imported_vpa)); ASSERT_FALSE(imported_credit_card); // |imported_credit_card_record_type_| should be NO_CARD because no valid card // was successfully imported from the form. @@ -2409,10 +2418,11 @@ TEST_F( FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); std::unique_ptr<CreditCard> imported_credit_card; + base::Optional<std::string> imported_vpa; EXPECT_TRUE(form_data_importer_->ImportFormData( form_structure, /*profile_autofill_enabled=*/true, /*credit_card_autofill_enabled=*/true, - /*should_return_local_card=*/true, &imported_credit_card)); + /*should_return_local_card=*/true, &imported_credit_card, &imported_vpa)); ASSERT_TRUE(imported_credit_card); // |imported_credit_card_record_type_| should be NEW_CARD because card was // successfully imported from the form via the expiration date fix flow. @@ -2450,10 +2460,11 @@ TEST_F(FormDataImporterTest, FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); std::unique_ptr<CreditCard> imported_credit_card; + base::Optional<std::string> imported_vpa; EXPECT_TRUE(form_data_importer_->ImportFormData( form_structure, /*profile_autofill_enabled=*/true, /*credit_card_autofill_enabled=*/true, - /*should_return_local_card=*/true, &imported_credit_card)); + /*should_return_local_card=*/true, &imported_credit_card, &imported_vpa)); ASSERT_FALSE(imported_credit_card); // |imported_credit_card_record_type_| should be NO_CARD because the form // doesn't have credit card section. @@ -2497,11 +2508,13 @@ TEST_F(FormDataImporterTest, ImportFormData_OneAddressOneCreditCard) { FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); std::unique_ptr<CreditCard> imported_credit_card; + base::Optional<std::string> imported_vpa; EXPECT_TRUE(form_data_importer_->ImportFormData( form_structure, /*profile_autofill_enabled=*/true, /*credit_card_autofill_enabled=*/true, - /*should_return_local_card=*/false, &imported_credit_card)); + /*should_return_local_card=*/false, &imported_credit_card, + &imported_vpa)); ASSERT_TRUE(imported_credit_card); personal_data_manager_->OnAcceptedLocalCreditCardSave(*imported_credit_card); @@ -2583,11 +2596,13 @@ TEST_F(FormDataImporterTest, ImportFormData_TwoAddressesOneCreditCard) { .WillRepeatedly(QuitMessageLoop(&run_loop)); EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()) .Times(testing::AnyNumber()); + base::Optional<std::string> imported_vpa; // Still returns true because the credit card import was successful. EXPECT_TRUE(form_data_importer_->ImportFormData( form_structure, /*profile_autofill_enabled=*/true, /*credit_card_autofill_enabled=*/true, - /*should_return_local_card=*/false, &imported_credit_card)); + /*should_return_local_card=*/false, &imported_credit_card, + &imported_vpa)); run_loop.Run(); ASSERT_TRUE(imported_credit_card); @@ -2642,10 +2657,12 @@ TEST_F(FormDataImporterTest, ImportFormData_AddressesDisabledOneCreditCard) { FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); std::unique_ptr<CreditCard> imported_credit_card; + base::Optional<std::string> imported_vpa; EXPECT_TRUE(form_data_importer_->ImportFormData( form_structure, /*profile_autofill_enabled=*/false, /*credit_card_autofill_enabled=*/true, - /*should_return_local_card=*/false, &imported_credit_card)); + /*should_return_local_card=*/false, &imported_credit_card, + &imported_vpa)); ASSERT_TRUE(imported_credit_card); personal_data_manager_->OnAcceptedLocalCreditCardSave(*imported_credit_card); @@ -2698,11 +2715,13 @@ TEST_F(FormDataImporterTest, ImportFormData_OneAddressCreditCardDisabled) { FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); std::unique_ptr<CreditCard> imported_credit_card; + base::Optional<std::string> imported_vpa; EXPECT_TRUE(form_data_importer_->ImportFormData( form_structure, /*profile_autofill_enabled=*/true, /*credit_card_autofill_enabled=*/false, - /*should_return_local_card=*/false, &imported_credit_card)); + /*should_return_local_card=*/false, &imported_credit_card, + &imported_vpa)); ASSERT_FALSE(imported_credit_card); WaitForOnPersonalDataChanged(); @@ -2758,11 +2777,13 @@ TEST_F(FormDataImporterTest, ImportFormData_AddressCreditCardDisabled) { FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); std::unique_ptr<CreditCard> imported_credit_card; + base::Optional<std::string> imported_vpa; EXPECT_FALSE(form_data_importer_->ImportFormData( form_structure, /*profile_autofill_enabled=*/false, /*credit_card_autofill_enabled=*/false, - /*should_return_local_card=*/false, &imported_credit_card)); + /*should_return_local_card=*/false, &imported_credit_card, + &imported_vpa)); ASSERT_FALSE(imported_credit_card); // Test that addresses were not saved. @@ -2815,10 +2836,12 @@ TEST_F(FormDataImporterTest, DontDuplicateMaskedServerCard) { FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); std::unique_ptr<CreditCard> imported_credit_card; + base::Optional<std::string> imported_vpa; EXPECT_FALSE(form_data_importer_->ImportFormData( form_structure, /*profile_autofill_enabled=*/true, /*credit_card_autofill_enabled=*/true, - /*should_return_local_card=*/false, &imported_credit_card)); + /*should_return_local_card=*/false, &imported_credit_card, + &imported_vpa)); ASSERT_FALSE(imported_credit_card); } @@ -2852,11 +2875,13 @@ TEST_F(FormDataImporterTest, ImportFormData_HiddenCreditCardFormAfterEntered) { FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); std::unique_ptr<CreditCard> imported_credit_card; + base::Optional<std::string> imported_vpa; // Still returns true because the credit card import was successful. EXPECT_TRUE(form_data_importer_->ImportFormData( form_structure, /*profile_autofill_enabled=*/true, /*credit_card_autofill_enabled=*/true, - /*should_return_local_card=*/false, &imported_credit_card)); + /*should_return_local_card=*/false, &imported_credit_card, + &imported_vpa)); ASSERT_TRUE(imported_credit_card); personal_data_manager_->OnAcceptedLocalCreditCardSave(*imported_credit_card); @@ -2872,6 +2897,27 @@ TEST_F(FormDataImporterTest, ImportFormData_HiddenCreditCardFormAfterEntered) { EXPECT_EQ(0, expected_card.Compare(*results[0])); } +// Ensures that no VPA value is returned when there's a credit card and no VPA. +TEST_F(FormDataImporterTest, + ImportFormData_DontSetVPAWhenOnlyCreditCardExists) { + // Simulate a form submission with a new credit card. + FormData form; + form.url = GURL("https://wwww.foo.com"); + + AddFullCreditCardForm(&form, "Biggie Smalls", "4111 1111 1111 1111", "01", + "2999"); + + FormStructure form_structure(form); + form_structure.DetermineHeuristicTypes(); + std::unique_ptr<CreditCard> imported_credit_card; + base::Optional<std::string> imported_vpa; + EXPECT_TRUE(form_data_importer_->ImportFormData( + form_structure, /*profile_autofill_enabled=*/true, + /*credit_card_autofill_enabled=*/true, + /*should_return_local_card=*/true, &imported_credit_card, &imported_vpa)); + ASSERT_FALSE(imported_vpa.has_value()); +} + TEST_F(FormDataImporterTest, DontDuplicateFullServerCard) { EnableWalletCardImport(); @@ -2914,11 +2960,13 @@ TEST_F(FormDataImporterTest, DontDuplicateFullServerCard) { FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); std::unique_ptr<CreditCard> imported_credit_card; + base::Optional<std::string> imported_vpa; EXPECT_FALSE(form_data_importer_->ImportFormData( form_structure, /*profile_autofill_enabled=*/true, /*credit_card_autofill_enabled=*/true, - /*should_return_local_card=*/false, &imported_credit_card)); + /*should_return_local_card=*/false, &imported_credit_card, + &imported_vpa)); EXPECT_FALSE(imported_credit_card); } @@ -2959,11 +3007,13 @@ TEST_F(FormDataImporterTest, FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); std::unique_ptr<CreditCard> imported_credit_card; + base::Optional<std::string> imported_vpa; EXPECT_FALSE(form_data_importer_->ImportFormData( form_structure, /*profile_autofill_enabled=*/true, /*credit_card_autofill_enabled=*/true, - /*should_return_local_card=*/false, &imported_credit_card)); + /*should_return_local_card=*/false, &imported_credit_card, + &imported_vpa)); EXPECT_FALSE(imported_credit_card); histogram_tester.ExpectUniqueSample( "Autofill.SubmittedServerCardExpirationStatus", @@ -3010,11 +3060,13 @@ TEST_F(FormDataImporterTest, FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); std::unique_ptr<CreditCard> imported_credit_card; + base::Optional<std::string> imported_vpa; EXPECT_FALSE(form_data_importer_->ImportFormData( form_structure, /*profile_autofill_enabled=*/true, /*credit_card_autofill_enabled=*/true, - /*should_return_local_card=*/false, &imported_credit_card)); + /*should_return_local_card=*/false, &imported_credit_card, + &imported_vpa)); EXPECT_FALSE(imported_credit_card); } @@ -3058,11 +3110,13 @@ TEST_F(FormDataImporterTest, FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); std::unique_ptr<CreditCard> imported_credit_card; + base::Optional<std::string> imported_vpa; EXPECT_FALSE(form_data_importer_->ImportFormData( form_structure, /*profile_autofill_enabled=*/true, /*credit_card_autofill_enabled=*/true, - /*should_return_local_card=*/false, &imported_credit_card)); + /*should_return_local_card=*/false, &imported_credit_card, + &imported_vpa)); EXPECT_FALSE(imported_credit_card); } @@ -3107,11 +3161,13 @@ TEST_F( FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); std::unique_ptr<CreditCard> imported_credit_card; + base::Optional<std::string> imported_vpa; EXPECT_TRUE(form_data_importer_->ImportFormData( form_structure, /*profile_autofill_enabled=*/true, /*credit_card_autofill_enabled=*/true, - /*should_return_local_card=*/false, &imported_credit_card)); + /*should_return_local_card=*/false, &imported_credit_card, + &imported_vpa)); EXPECT_TRUE(imported_credit_card); } @@ -3153,11 +3209,13 @@ TEST_F(FormDataImporterTest, FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); std::unique_ptr<CreditCard> imported_credit_card; + base::Optional<std::string> imported_vpa; EXPECT_FALSE(form_data_importer_->ImportFormData( form_structure, /*profile_autofill_enabled=*/true, /*credit_card_autofill_enabled=*/true, - /*should_return_local_card=*/false, &imported_credit_card)); + /*should_return_local_card=*/false, &imported_credit_card, + &imported_vpa)); EXPECT_FALSE(imported_credit_card); histogram_tester.ExpectUniqueSample( "Autofill.SubmittedServerCardExpirationStatus", @@ -3202,11 +3260,13 @@ TEST_F(FormDataImporterTest, FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); std::unique_ptr<CreditCard> imported_credit_card; + base::Optional<std::string> imported_vpa; EXPECT_FALSE(form_data_importer_->ImportFormData( form_structure, /*profile_autofill_enabled=*/true, /*credit_card_autofill_enabled=*/true, - /*should_return_local_card=*/false, &imported_credit_card)); + /*should_return_local_card=*/false, &imported_credit_card, + &imported_vpa)); EXPECT_FALSE(imported_credit_card); histogram_tester.ExpectUniqueSample( "Autofill.SubmittedServerCardExpirationStatus", @@ -3252,14 +3312,86 @@ TEST_F(FormDataImporterTest, FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); std::unique_ptr<CreditCard> imported_credit_card; + base::Optional<std::string> imported_vpa; EXPECT_FALSE(form_data_importer_->ImportFormData( form_structure, /*profile_autofill_enabled=*/true, /*credit_card_autofill_enabled=*/true, - /*should_return_local_card=*/false, &imported_credit_card)); + /*should_return_local_card=*/false, &imported_credit_card, + &imported_vpa)); EXPECT_FALSE(imported_credit_card); histogram_tester.ExpectUniqueSample( "Autofill.SubmittedServerCardExpirationStatus", AutofillMetrics::MASKED_SERVER_CARD_EXPIRATION_DATE_DID_NOT_MATCH, 1); } +TEST_F(FormDataImporterTest, ImportVPA) { + FormData form; + form.url = GURL("https://wwww.foo.com"); + + FormFieldData field; + test::CreateTestFormField("VPA:", "vpa", "user@indianbank", "text", &field); + form.fields.push_back(field); + + FormStructure form_structure(form); + form_structure.DetermineHeuristicTypes(); + + std::unique_ptr<CreditCard> imported_credit_card; // Discarded. + base::Optional<std::string> imported_vpa; + + EXPECT_TRUE(form_data_importer_->ImportFormData( + form_structure, /*profile_autofill_enabled=*/false, + /*credit_card_autofill_enabled=*/true, + /*should_return_local_card=*/false, &imported_credit_card, + &imported_vpa)); + + ASSERT_TRUE(imported_vpa.has_value()); + EXPECT_EQ(imported_vpa.value(), "user@indianbank"); +} + +TEST_F(FormDataImporterTest, ImportVPADisabled) { + FormData form; + form.url = GURL("https://wwww.foo.com"); + + FormFieldData field; + test::CreateTestFormField("VPA:", "vpa", "user@indianbank", "text", &field); + form.fields.push_back(field); + + FormStructure form_structure(form); + form_structure.DetermineHeuristicTypes(); + + std::unique_ptr<CreditCard> imported_credit_card; // Discarded. + base::Optional<std::string> imported_vpa; + + EXPECT_FALSE(form_data_importer_->ImportFormData( + form_structure, /*profile_autofill_enabled=*/false, + /*credit_card_autofill_enabled=*/false, + /*should_return_local_card=*/false, &imported_credit_card, + &imported_vpa)); + + EXPECT_FALSE(imported_vpa.has_value()); +} + +TEST_F(FormDataImporterTest, ImportVPAIgnoreNonVPA) { + FormData form; + form.url = GURL("https://wwww.foo.com"); + + FormFieldData field; + test::CreateTestFormField("VPA:", "vpa", "user@gmail.com", "text", &field); + form.fields.push_back(field); + + FormStructure form_structure(form); + form_structure.DetermineHeuristicTypes(); + + std::unique_ptr<CreditCard> imported_credit_card; // Discarded. + base::Optional<std::string> imported_vpa; + + EXPECT_FALSE(form_data_importer_->ImportFormData( + form_structure, /*profile_autofill_enabled=*/false, + /*credit_card_autofill_enabled=*/false, + /*should_return_local_card=*/false, &imported_credit_card, + &imported_vpa)); + + EXPECT_FALSE(imported_vpa.has_value()); +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/form_parsing/autofill_scanner.h b/chromium/components/autofill/core/browser/form_parsing/autofill_scanner.h index 336e5562852..5adf96d9ccd 100644 --- a/chromium/components/autofill/core/browser/form_parsing/autofill_scanner.h +++ b/chromium/components/autofill/core/browser/form_parsing/autofill_scanner.h @@ -45,9 +45,6 @@ class AutofillScanner { // |RewindTo()|. size_t SaveCursor(); - // This is only for logging purposes. - size_t CursorIndex() { return static_cast<size_t>(cursor_ - begin_); } - private: void Init(const std::vector<AutofillField*>& fields); diff --git a/chromium/components/autofill/core/browser/form_parsing/field_candidates.cc b/chromium/components/autofill/core/browser/form_parsing/field_candidates.cc index f3dc3b5b9dc..e60965ab5a8 100644 --- a/chromium/components/autofill/core/browser/form_parsing/field_candidates.cc +++ b/chromium/components/autofill/core/browser/form_parsing/field_candidates.cc @@ -45,8 +45,4 @@ ServerFieldType FieldCandidates::BestHeuristicType() const { return static_cast<ServerFieldType>(index); } -const std::vector<FieldCandidate>& FieldCandidates::field_candidates() const { - return field_candidates_; -} - } // namespace autofill diff --git a/chromium/components/autofill/core/browser/form_parsing/field_candidates.h b/chromium/components/autofill/core/browser/form_parsing/field_candidates.h index d4520921f42..f1ef7246213 100644 --- a/chromium/components/autofill/core/browser/form_parsing/field_candidates.h +++ b/chromium/components/autofill/core/browser/form_parsing/field_candidates.h @@ -44,11 +44,6 @@ class FieldCandidates { // Determines the best type based on the current possible types. ServerFieldType BestHeuristicType() const; - // Returns the underlying candidates. - // - // This reference is only valid while the FieldCandidates object is valid. - const std::vector<FieldCandidate>& field_candidates() const; - private: // Internal storage for all the possible types for a given field. std::vector<FieldCandidate> field_candidates_; diff --git a/chromium/components/autofill/core/browser/form_parsing/form_field.cc b/chromium/components/autofill/core/browser/form_parsing/form_field.cc index 266bd688bfe..d801580967b 100644 --- a/chromium/components/autofill/core/browser/form_parsing/form_field.cc +++ b/chromium/components/autofill/core/browser/form_parsing/form_field.cc @@ -17,7 +17,6 @@ #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/autofill_field.h" -#include "components/autofill/core/browser/autofill_internals_service.h" #include "components/autofill/core/browser/form_parsing/address_field.h" #include "components/autofill/core/browser/form_parsing/autofill_scanner.h" #include "components/autofill/core/browser/form_parsing/credit_card_field.h" diff --git a/chromium/components/autofill/core/browser/form_structure.cc b/chromium/components/autofill/core/browser/form_structure.cc index 22b536cd140..542afd7f8b3 100644 --- a/chromium/components/autofill/core/browser/form_structure.cc +++ b/chromium/components/autofill/core/browser/form_structure.cc @@ -35,21 +35,25 @@ #include "components/autofill/core/browser/field_types.h" #include "components/autofill/core/browser/form_parsing/field_candidates.h" #include "components/autofill/core/browser/form_parsing/form_field.h" -#include "components/autofill/core/browser/logging/log_buffer.h" +#include "components/autofill/core/browser/logging/log_manager.h" #include "components/autofill/core/browser/proto/legacy_proto_bridge.h" #include "components/autofill/core/browser/randomized_encoder.h" #include "components/autofill/core/browser/rationalization_util.h" #include "components/autofill/core/browser/validation.h" #include "components/autofill/core/common/autofill_constants.h" #include "components/autofill/core/common/autofill_features.h" +#include "components/autofill/core/common/autofill_internals/log_message.h" +#include "components/autofill/core/common/autofill_internals/logging_scope.h" #include "components/autofill/core/common/autofill_payments_features.h" #include "components/autofill/core/common/autofill_regex_constants.h" #include "components/autofill/core/common/autofill_regexes.h" +#include "components/autofill/core/common/autofill_tick_clock.h" #include "components/autofill/core/common/autofill_util.h" #include "components/autofill/core/common/form_data.h" #include "components/autofill/core/common/form_data_predictions.h" #include "components/autofill/core/common/form_field_data.h" #include "components/autofill/core/common/form_field_data_predictions.h" +#include "components/autofill/core/common/logging/log_buffer.h" #include "components/autofill/core/common/signatures_util.h" #include "components/security_state/core/security_state.h" #include "url/origin.h" @@ -409,13 +413,6 @@ void EncodePasswordAttributesVote( upload->set_password_has_lowercase_letter( password_attributes_vote.second); break; - case PasswordAttribute::kHasUppercaseLetter: - upload->set_password_has_uppercase_letter( - password_attributes_vote.second); - break; - case PasswordAttribute::kHasNumeric: - upload->set_password_has_numeric(password_attributes_vote.second); - break; case PasswordAttribute::kHasSpecialSymbol: upload->set_password_has_special_symbol(password_attributes_vote.second); if (password_attributes_vote.second) @@ -568,22 +565,13 @@ FormStructure::FormStructure(const FormData& form) name_attribute_(form.name_attribute), form_name_(form.name), button_titles_(form.button_titles), - submission_event_(SubmissionIndicatorEvent::NONE), source_url_(form.url), target_url_(form.action), main_frame_origin_(form.main_frame_origin), - autofill_count_(0), - active_field_count_(0), - upload_required_(USE_UPLOAD_RATES), - has_author_specified_types_(false), - has_author_specified_sections_(false), - has_author_specified_upi_vpa_hint_(false), - was_parsed_for_autocomplete_attributes_(false), - has_password_field_(false), is_form_tag_(form.is_form_tag), is_formless_checkout_(form.is_formless_checkout), all_fields_are_passwords_(!form.fields.empty()), - form_parsed_timestamp_(base::TimeTicks::Now()), + form_parsed_timestamp_(AutofillTickClock::NowTicks()), passwords_were_revealed_(false), password_symbol_vote_(0), developer_engagement_metrics_(0), @@ -613,10 +601,19 @@ FormStructure::FormStructure(const FormData& form) ProcessExtractedFields(); } -FormStructure::~FormStructure() {} +FormStructure::FormStructure( + FormSignature form_signature, + const std::vector<FieldSignature>& field_signatures) + : form_signature_(form_signature) { + for (const auto& signature : field_signatures) + fields_.push_back(AutofillField::CreateForPasswordManagerUpload(signature)); +} + +FormStructure::~FormStructure() = default; void FormStructure::DetermineHeuristicTypes(LogManager* log_manager) { - const auto determine_heuristic_types_start_time = base::TimeTicks::Now(); + const auto determine_heuristic_types_start_time = + AutofillTickClock::NowTicks(); // First, try to detect field types based on each field's |autocomplete| // attribute value. @@ -660,7 +657,7 @@ void FormStructure::DetermineHeuristicTypes(LogManager* log_manager) { RationalizeFieldTypePredictions(); AutofillMetrics::LogDetermineHeuristicTypesTiming( - base::TimeTicks::Now() - determine_heuristic_types_start_time); + AutofillTickClock::NowTicks() - determine_heuristic_types_start_time); } bool FormStructure::EncodeUploadRequest( @@ -826,6 +823,14 @@ void FormStructure::ProcessQueryResponse( if (heuristic_type != UNKNOWN_TYPE) heuristics_detected_fillable_field = true; + // Clears the server prediction for CVC-fields if the corresponding Finch + // feature is not enabled. + if (!base::FeatureList::IsEnabled( + autofill::features::kAutofillUseServerCVCPrediction) && + field_type == ServerFieldType::CREDIT_CARD_VERIFICATION_CODE) { + field_type = ServerFieldType::NO_SERVER_DATA; + } + field->set_server_type(field_type); std::vector<AutofillQueryResponseContents::Field::FieldPrediction> server_predictions; @@ -913,6 +918,14 @@ bool FormStructure::IsAutofillFieldMetadataEnabled() { return base::StartsWith(group_name, "Enabled", base::CompareCase::SENSITIVE); } +std::unique_ptr<FormStructure> FormStructure::CreateForPasswordManagerUpload( + FormSignature form_signature, + const std::vector<FieldSignature>& field_signatures) { + std::unique_ptr<FormStructure> form; + form.reset(new FormStructure(form_signature, field_signatures)); + return form; +} + std::string FormStructure::FormSignatureAsStr() const { return base::NumberToString(form_signature()); } @@ -951,10 +964,15 @@ void FormStructure::UpdateAutofillCount() { } } -bool FormStructure::ShouldBeParsed() const { +bool FormStructure::ShouldBeParsed(LogManager* log_manager) const { // Exclude URLs not on the web via HTTP(S). - if (!HasAllowedScheme(source_url_)) + if (!HasAllowedScheme(source_url_)) { + if (log_manager) { + log_manager->Log() << LoggingScope::kAbortParsing + << LogMessage::kAbortParsingNotAllowedScheme << *this; + } return false; + } size_t min_required_fields = std::min({MinRequiredFieldsForHeuristics(), MinRequiredFieldsForQuery(), @@ -963,6 +981,11 @@ bool FormStructure::ShouldBeParsed() const { (!all_fields_are_passwords() || active_field_count() < kRequiredFieldsForFormsWithOnlyPasswordFields) && !has_author_specified_types_) { + if (log_manager) { + log_manager->Log() << LoggingScope::kAbortParsing + << LogMessage::kAbortParsingNotEnoughFields + << active_field_count() << *this; + } return false; } @@ -971,6 +994,11 @@ bool FormStructure::ShouldBeParsed() const { base::UTF8ToUTF16(kUrlSearchActionRe); if (MatchesPattern(base::UTF8ToUTF16(target_url_.path_piece()), kUrlSearchActionPattern)) { + if (log_manager) { + log_manager->Log() << LoggingScope::kAbortParsing + << LogMessage::kAbortParsingUrlMatchesSearchRegex + << *this; + } return false; } @@ -979,6 +1007,11 @@ bool FormStructure::ShouldBeParsed() const { has_text_field |= it->form_control_type != "select-one"; } + if (!has_text_field && log_manager) { + log_manager->Log() << LoggingScope::kAbortParsing + << LogMessage::kAbortParsingFormHasNoTextfield << *this; + } + return has_text_field; } @@ -1031,9 +1064,7 @@ void FormStructure::RetrieveFromCache( bool is_credit_card_field = AutofillType(cached_field->second->Type().GetStorableType()) .group() == CREDIT_CARD; - if (should_keep_cached_value && is_credit_card_field && - base::FeatureList::IsEnabled( - features::kAutofillImportDynamicForms)) { + if (should_keep_cached_value && is_credit_card_field) { field->value = cached_field->second->value; value_from_dynamic_change_form_ = true; } else if (field->value == cached_field->second->value) { @@ -2194,6 +2225,7 @@ LogBuffer& operator<<(LogBuffer& buffer, const FormStructure& form) { base::NumberToString( HashFormSignature(form.form_signature()))}); buffer << Tr{} << "Form name:" << form.form_name(); + buffer << Tr{} << "Unique renderer Id:" << form.unique_renderer_id(); buffer << Tr{} << "Target URL:" << form.target_url(); for (size_t i = 0; i < form.field_count(); ++i) { buffer << Tag{"tr"}; diff --git a/chromium/components/autofill/core/browser/form_structure.h b/chromium/components/autofill/core/browser/form_structure.h index 7a90138c297..4117c49e0aa 100644 --- a/chromium/components/autofill/core/browser/form_structure.h +++ b/chromium/components/autofill/core/browser/form_structure.h @@ -44,8 +44,6 @@ class LogManager; // Password attributes (whether a password has special symbols, numeric, etc.) enum class PasswordAttribute { kHasLowercaseLetter, - kHasUppercaseLetter, - kHasNumeric, kHasSpecialSymbol, kPasswordAttributesCount }; @@ -111,6 +109,13 @@ class FormStructure { // Returns whether sending autofill field metadata to the server is enabled. static bool IsAutofillFieldMetadataEnabled(); + // Creates FormStructure that has bare minimum information for uploading + // votes, namely form and field signatures. Warning: do not use for Autofill + // code, since it is likely missing some fields. + static std::unique_ptr<FormStructure> CreateForPasswordManagerUpload( + FormSignature form_signature, + const std::vector<FieldSignature>& field_signatures); + // Return the form signature as string. std::string FormSignatureAsStr() const; @@ -130,7 +135,7 @@ class FormStructure { void UpdateAutofillCount(); // Returns true if this form matches the structural requirements for Autofill. - bool ShouldBeParsed() const; + bool ShouldBeParsed(LogManager* log_manager = nullptr) const; // Returns true if heuristic autofill type detection should be attempted for // this form. @@ -237,10 +242,6 @@ class FormStructure { return has_author_specified_types_; } - bool has_author_specified_sections() const { - return has_author_specified_sections_; - } - bool has_author_specified_upi_vpa_hint() const { return has_author_specified_upi_vpa_hint_; } @@ -414,6 +415,9 @@ class FormStructure { size_t current_section_ptr = 0; }; + FormStructure(FormSignature form_signature, + const std::vector<FieldSignature>& field_signatures); + // A function to fine tune the credit cards related predictions. For example: // lone credit card fields in an otherwise non-credit-card related form is // unlikely to be correct, the function will override that prediction. @@ -526,7 +530,8 @@ class FormStructure { // The type of the event that was taken as an indication that the form has // been successfully submitted. - mojom::SubmissionIndicatorEvent submission_event_; + mojom::SubmissionIndicatorEvent submission_event_ = + mojom::SubmissionIndicatorEvent::NONE; // The source URL. GURL source_url_; @@ -538,50 +543,50 @@ class FormStructure { url::Origin main_frame_origin_; // The number of fields able to be auto-filled. - size_t autofill_count_; + size_t autofill_count_ = 0; // A vector of all the input fields in the form. std::vector<std::unique_ptr<AutofillField>> fields_; // The number of fields that are part of the form signature and that are // included in queries to the Autofill server. - size_t active_field_count_; + size_t active_field_count_ = 0; // Whether the server expects us to always upload, never upload, or default // to the stored upload rates. - UploadRequired upload_required_; + UploadRequired upload_required_ = USE_UPLOAD_RATES; // Whether the form includes any field types explicitly specified by the site // author, via the |autocompletetype| attribute. - bool has_author_specified_types_; + bool has_author_specified_types_ = false; // Whether the form includes any sections explicitly specified by the site // author, via the autocomplete attribute. - bool has_author_specified_sections_; + bool has_author_specified_sections_ = false; // Whether the form includes a field that explicitly sets it autocomplete // type to "upi-vpa". - bool has_author_specified_upi_vpa_hint_; + bool has_author_specified_upi_vpa_hint_ = false; // Whether the form was parsed for autocomplete attribute, thus assigning // the real values of |has_author_specified_types_| and // |has_author_specified_sections_|. - bool was_parsed_for_autocomplete_attributes_; + bool was_parsed_for_autocomplete_attributes_ = false; // True if the form contains at least one password field. - bool has_password_field_; + bool has_password_field_ = false; // True if the form is a <form>. - bool is_form_tag_; + bool is_form_tag_ = true; // True if the form is made of unowned fields (i.e., not within a <form> tag) // in what appears to be a checkout flow. This attribute is only calculated // and used if features::kAutofillRestrictUnownedFieldsToFormlessCheckout is // enabled, to prevent heuristics from running on formless non-checkout. - bool is_formless_checkout_; + bool is_formless_checkout_ = false; // True if all form fields are password fields. - bool all_fields_are_passwords_; + bool all_fields_are_passwords_ = false; // The unique signature for this form, composed of the target url domain, // the form name, and the form field names in a 64-bit hash. @@ -595,7 +600,7 @@ class FormStructure { // True iff the form is a password form and the user has seen the password // value before accepting the prompt to save. Used for crowdsourcing. - bool passwords_were_revealed_; + bool passwords_were_revealed_ = false; // The vote about password attributes (e.g. whether the password has a numeric // character). @@ -605,7 +610,7 @@ class FormStructure { // field contains nosified information about a special symbol in a // user-created password stored as ASCII code. The default value of 0 // indicates that no symbol was set. - int password_symbol_vote_; + int password_symbol_vote_ = 0; // Noisified password length for crowdsourcing. If |password_attributes_vote_| // has no value, |password_length_vote_| should be ignored. @@ -614,7 +619,7 @@ class FormStructure { // Used to record whether developer has used autocomplete markup or // UPI-VPA hints, This is a bitmask of DeveloperEngagementMetric and set in // DetermineHeuristicTypes(). - int developer_engagement_metrics_; + int developer_engagement_metrics_ = 0; mojom::SubmissionSource submission_source_ = mojom::SubmissionSource::NONE; @@ -634,7 +639,6 @@ class FormStructure { }; LogBuffer& operator<<(LogBuffer& buffer, const FormStructure& form); - } // namespace autofill #endif // COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_STRUCTURE_H_ diff --git a/chromium/components/autofill/core/browser/form_structure_unittest.cc b/chromium/components/autofill/core/browser/form_structure_unittest.cc index 7ebf1c72000..f0b086f2d8c 100644 --- a/chromium/components/autofill/core/browser/form_structure_unittest.cc +++ b/chromium/components/autofill/core/browser/form_structure_unittest.cc @@ -2592,7 +2592,7 @@ TEST_F(FormStructureTest, EncodeUploadRequest_WithMatchingValidities) { form_structure = std::make_unique<FormStructure>(form); form_structure->set_password_attributes_vote( - std::make_pair(PasswordAttribute::kHasNumeric, true)); + std::make_pair(PasswordAttribute::kHasLowercaseLetter, true)); form_structure->set_password_length_vote(10u); ASSERT_EQ(form_structure->field_count(), possible_field_types.size()); @@ -2623,7 +2623,7 @@ TEST_F(FormStructureTest, EncodeUploadRequest_WithMatchingValidities) { upload.set_autofill_used(false); upload.set_data_present("144200030e"); upload.set_passwords_revealed(false); - upload.set_password_has_numeric(true); + upload.set_password_has_lowercase_letter(true); upload.set_password_length(10u); upload.set_action_signature(15724779818122431245U); upload.set_submission_event( @@ -2685,7 +2685,7 @@ TEST_F(FormStructureTest, EncodeUploadRequest_WithMatchingValidities) { form_structure = std::make_unique<FormStructure>(form); form_structure->set_password_attributes_vote( - std::make_pair(PasswordAttribute::kHasNumeric, true)); + std::make_pair(PasswordAttribute::kHasLowercaseLetter, true)); form_structure->set_password_length_vote(10u); ASSERT_EQ(form_structure->field_count(), possible_field_types.size()); @@ -2787,7 +2787,7 @@ TEST_F(FormStructureTest, EncodeUploadRequest_WithNonMatchingValidities) { form_structure = std::make_unique<FormStructure>(form); form_structure->set_password_attributes_vote( - std::make_pair(PasswordAttribute::kHasNumeric, true)); + std::make_pair(PasswordAttribute::kHasLowercaseLetter, true)); form_structure->set_password_length_vote(10u); ASSERT_EQ(form_structure->field_count(), possible_field_types.size()); @@ -2818,7 +2818,7 @@ TEST_F(FormStructureTest, EncodeUploadRequest_WithNonMatchingValidities) { upload.set_autofill_used(false); upload.set_data_present("144200030e"); upload.set_passwords_revealed(false); - upload.set_password_has_numeric(true); + upload.set_password_has_lowercase_letter(true); upload.set_password_length(10u); upload.set_action_signature(15724779818122431245U); @@ -2918,7 +2918,7 @@ TEST_F(FormStructureTest, EncodeUploadRequest_WithMultipleValidities) { form_structure = std::make_unique<FormStructure>(form); form_structure->set_password_attributes_vote( - std::make_pair(PasswordAttribute::kHasNumeric, true)); + std::make_pair(PasswordAttribute::kHasLowercaseLetter, true)); form_structure->set_password_length_vote(10u); ASSERT_EQ(form_structure->field_count(), possible_field_types.size()); @@ -2949,7 +2949,7 @@ TEST_F(FormStructureTest, EncodeUploadRequest_WithMultipleValidities) { upload.set_autofill_used(false); upload.set_data_present("144200030e"); upload.set_passwords_revealed(false); - upload.set_password_has_numeric(true); + upload.set_password_has_lowercase_letter(true); upload.set_password_length(10u); upload.set_action_signature(15724779818122431245U); upload.set_submission_event( @@ -3044,7 +3044,7 @@ TEST_F(FormStructureTest, EncodeUploadRequest) { form_structure = std::make_unique<FormStructure>(form); form_structure->set_password_attributes_vote( - std::make_pair(PasswordAttribute::kHasNumeric, true)); + std::make_pair(PasswordAttribute::kHasLowercaseLetter, true)); form_structure->set_password_length_vote(10u); form_structure->set_submission_event( SubmissionIndicatorEvent::HTML_FORM_SUBMISSION); @@ -3078,7 +3078,7 @@ TEST_F(FormStructureTest, EncodeUploadRequest) { upload.set_autofill_used(false); upload.set_data_present("144200030e"); upload.set_passwords_revealed(false); - upload.set_password_has_numeric(true); + upload.set_password_has_lowercase_letter(true); upload.set_password_length(10u); upload.set_action_signature(15724779818122431245U); upload.set_has_form_tag(true); @@ -3130,7 +3130,7 @@ TEST_F(FormStructureTest, EncodeUploadRequest) { form_structure = std::make_unique<FormStructure>(form); form_structure->set_password_attributes_vote( - std::make_pair(PasswordAttribute::kHasNumeric, true)); + std::make_pair(PasswordAttribute::kHasLowercaseLetter, true)); form_structure->set_password_length_vote(10u); form_structure->set_submission_event( SubmissionIndicatorEvent::HTML_FORM_SUBMISSION); @@ -7123,4 +7123,19 @@ TEST_F(FormStructureTest, OneFieldPasswordFormShouldNotBeUpload) { EXPECT_FALSE(FormStructure(form).ShouldBeUploaded()); } +// Checks that CreateForPasswordManagerUpload builds FormStructure +// which is encodable (i.e. ready for uploading). +TEST_F(FormStructureTest, CreateForPasswordManagerUpload) { + std::unique_ptr<FormStructure> form = + FormStructure::CreateForPasswordManagerUpload( + 1234 /* form_signature */, {1, 10, 100} /* field_signatures */); + AutofillUploadContents upload; + EXPECT_EQ(1234u, form->form_signature()); + ASSERT_EQ(3u, form->field_count()); + ASSERT_EQ(100u, form->field(2)->GetFieldSignature()); + EXPECT_TRUE(form->EncodeUploadRequest( + {} /* available_field_types */, false /* form_was_autofilled */, + "" /*login_form_signature*/, true /*observed_submission*/, &upload)); +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/logging/log_buffer_submitter.h b/chromium/components/autofill/core/browser/logging/log_buffer_submitter.h index 5925e842eda..ef6c411bdd0 100644 --- a/chromium/components/autofill/core/browser/logging/log_buffer_submitter.h +++ b/chromium/components/autofill/core/browser/logging/log_buffer_submitter.h @@ -6,7 +6,7 @@ #define COMPONENTS_AUTOFILL_CORE_BROWSER_LOGGING_LOG_BUFFER_SUBMITTER_H_ #include "base/macros.h" -#include "components/autofill/core/browser/logging/log_buffer.h" +#include "components/autofill/core/common/logging/log_buffer.h" namespace autofill { diff --git a/chromium/components/autofill/core/browser/logging/log_protobufs.h b/chromium/components/autofill/core/browser/logging/log_protobufs.h new file mode 100644 index 00000000000..a2008a3a2fa --- /dev/null +++ b/chromium/components/autofill/core/browser/logging/log_protobufs.h @@ -0,0 +1,31 @@ +// Copyright 2019 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. + +#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_LOGGING_LOG_PROTOBUFS_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_LOGGING_LOG_PROTOBUFS_H_ + +#include "components/autofill/core/common/logging/log_buffer.h" +#include "third_party/protobuf/src/google/protobuf/repeated_field.h" + +namespace autofill { + +// Serialize a repeated field in a protobuf. This function is not part of +// log_buffer.h because that would create a dependency of the renderer process +// to protobufs. +template <typename T> +LogBuffer& operator<<(LogBuffer& buf, + const ::google::protobuf::RepeatedField<T>& values) { + buf << "["; + for (int i = 0; i < values.size(); ++i) { + if (i) + buf << ", "; + buf << values.Get(i); + } + buf << "]"; + return buf; +} + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_LOGGING_LOG_PROTOBUFS_H_ diff --git a/chromium/components/autofill/core/browser/logging/log_router.cc b/chromium/components/autofill/core/browser/logging/log_router.cc index 3ab3a1bfabc..f31dd7cb997 100644 --- a/chromium/components/autofill/core/browser/logging/log_router.cc +++ b/chromium/components/autofill/core/browser/logging/log_router.cc @@ -6,9 +6,9 @@ #include "base/stl_util.h" #include "base/strings/string_split.h" -#include "components/autofill/core/browser/logging/log_buffer.h" #include "components/autofill/core/browser/logging/log_manager.h" #include "components/autofill/core/browser/logging/log_receiver.h" +#include "components/autofill/core/common/logging/log_buffer.h" #include "net/base/escape.h" namespace autofill { diff --git a/chromium/components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.cc b/chromium/components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.cc index b969466c133..fcd2b80680a 100644 --- a/chromium/components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.cc +++ b/chromium/components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.cc @@ -10,9 +10,9 @@ #include "base/strings/utf_string_conversions.h" #include "base/values.h" #include "build/branding_buildflags.h" +#include "build/build_config.h" #include "components/autofill/core/browser/autofill_experiments.h" #include "components/autofill/core/browser/data_model/credit_card.h" -#include "components/autofill/core/browser/payments/legal_message_line.h" #include "components/autofill/core/common/autofill_constants.h" #include "components/autofill/core/common/autofill_features.h" #include "components/autofill/core/common/autofill_payments_features.h" @@ -32,7 +32,7 @@ AutofillSaveCardInfoBarDelegateMobile::AutofillSaveCardInfoBarDelegateMobile( bool upload, AutofillClient::SaveCreditCardOptions options, const CreditCard& card, - std::unique_ptr<base::DictionaryValue> legal_message, + const LegalMessageLines& legal_message_lines, AutofillClient::UploadSaveCardPromptCallback upload_save_card_prompt_callback, AutofillClient::LocalSaveCardPromptCallback local_save_card_prompt_callback, @@ -51,22 +51,11 @@ AutofillSaveCardInfoBarDelegateMobile::AutofillSaveCardInfoBarDelegateMobile( card_label_(card.NetworkAndLastFourDigits()), card_sub_label_(card.AbbreviatedExpirationDateForDisplay(false)), card_last_four_digits_(card.LastFourDigits()), + legal_message_lines_(legal_message_lines), is_off_the_record_(is_off_the_record) { DCHECK_EQ(upload, !upload_save_card_prompt_callback_.is_null()); DCHECK_EQ(upload, local_save_card_prompt_callback_.is_null()); - if (legal_message) { - if (!LegalMessageLine::Parse(*legal_message, &legal_messages_, - /*escape_apostrophes=*/true)) { - AutofillMetrics::LogCreditCardInfoBarMetric( - AutofillMetrics::INFOBAR_NOT_SHOWN_INVALID_LEGAL_MESSAGE, upload_, - options_, - pref_service_->GetInteger( - prefs::kAutofillAcceptSaveCreditCardPromptState)); - return; - } - } - AutofillMetrics::LogCreditCardInfoBarMetric( AutofillMetrics::INFOBAR_SHOWN, upload_, options_, pref_service_->GetInteger( @@ -86,12 +75,6 @@ void AutofillSaveCardInfoBarDelegateMobile::OnLegalMessageLinkClicked( infobar()->owner()->OpenURL(url, WindowOpenDisposition::NEW_FOREGROUND_TAB); } -bool AutofillSaveCardInfoBarDelegateMobile::LegalMessagesParsedSuccessfully() { - // If we are uploading to the server, verify that legal lines have been parsed - // into |legal_messages_|. - return !upload_ || !legal_messages_.empty(); -} - bool AutofillSaveCardInfoBarDelegateMobile::IsGooglePayBrandingEnabled() const { #if BUILDFLAG(GOOGLE_CHROME_BRANDING) return upload_; @@ -136,10 +119,22 @@ AutofillSaveCardInfoBarDelegateMobile::GetIdentifier() const { bool AutofillSaveCardInfoBarDelegateMobile::ShouldExpire( const NavigationDetails& details) const { +#if defined(OS_IOS) + if (base::FeatureList::IsEnabled( + features::kAutofillSaveCardDismissOnNavigation)) { + // Expire the Infobar unless the navigation was triggered by the form that + // presented the Infobar, or the navigation is a redirect. + return !details.is_form_submission && !details.is_redirect; + } else { + // Use the default behavior used by Android. + return false; + } +#else // defined(OS_IOS) // The user has submitted a form, causing the page to navigate elsewhere. We // don't want the infobar to be expired at this point, because the user won't // get a chance to answer the question. return false; +#endif // defined(OS_IOS) } void AutofillSaveCardInfoBarDelegateMobile::InfoBarDismissed() { diff --git a/chromium/components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.h b/chromium/components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.h index bd7e72e577c..f0dcb5f84f8 100644 --- a/chromium/components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.h +++ b/chromium/components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.h @@ -17,10 +17,6 @@ class PrefService; -namespace base { -class DictionaryValue; -} - namespace autofill { class CreditCard; @@ -33,7 +29,7 @@ class AutofillSaveCardInfoBarDelegateMobile : public ConfirmInfoBarDelegate { bool upload, AutofillClient::SaveCreditCardOptions options, const CreditCard& card, - std::unique_ptr<base::DictionaryValue> legal_message, + const LegalMessageLines& legal_message_lines, AutofillClient::UploadSaveCardPromptCallback upload_save_card_prompt_callback, AutofillClient::LocalSaveCardPromptCallback @@ -47,15 +43,13 @@ class AutofillSaveCardInfoBarDelegateMobile : public ConfirmInfoBarDelegate { int issuer_icon_id() const { return issuer_icon_id_; } const base::string16& card_label() const { return card_label_; } const base::string16& card_sub_label() const { return card_sub_label_; } - const LegalMessageLines& legal_messages() const { return legal_messages_; } + const LegalMessageLines& legal_message_lines() const { + return legal_message_lines_; + } // Called when a link in the legal message text was clicked. void OnLegalMessageLinkClicked(GURL url); - // Ensures the InfoBar is not shown if legal messages failed to parse. - // Legal messages are only specified for the upload case, not for local save. - bool LegalMessagesParsedSuccessfully(); - // Google Pay branding is enabled with a flag and only for cards upstreamed // to Google. bool IsGooglePayBrandingEnabled() const; @@ -117,8 +111,8 @@ class AutofillSaveCardInfoBarDelegateMobile : public ConfirmInfoBarDelegate { // The last four digits of the card for which save is being offered. base::string16 card_last_four_digits_; - // The legal messages to show in the content of the infobar. - LegalMessageLines legal_messages_; + // The legal message lines to show in the content of the infobar. + const LegalMessageLines& legal_message_lines_; // Whether the save is offered while off the record bool is_off_the_record_; diff --git a/chromium/components/autofill/core/browser/payments/autofill_wallet_model_type_controller.cc b/chromium/components/autofill/core/browser/payments/autofill_wallet_model_type_controller.cc index 787a9e3428d..263b8d89025 100644 --- a/chromium/components/autofill/core/browser/payments/autofill_wallet_model_type_controller.cc +++ b/chromium/components/autofill/core/browser/payments/autofill_wallet_model_type_controller.cc @@ -18,10 +18,11 @@ namespace browser_sync { AutofillWalletModelTypeController::AutofillWalletModelTypeController( syncer::ModelType type, - std::unique_ptr<syncer::ModelTypeControllerDelegate> delegate_on_disk, + std::unique_ptr<syncer::ModelTypeControllerDelegate> + delegate_for_full_sync_mode, PrefService* pref_service, syncer::SyncService* sync_service) - : ModelTypeController(type, std::move(delegate_on_disk)), + : ModelTypeController(type, std::move(delegate_for_full_sync_mode)), pref_service_(pref_service), sync_service_(sync_service) { DCHECK(type == syncer::AUTOFILL_WALLET_DATA || @@ -34,13 +35,15 @@ AutofillWalletModelTypeController::AutofillWalletModelTypeController( AutofillWalletModelTypeController::AutofillWalletModelTypeController( syncer::ModelType type, - std::unique_ptr<syncer::ModelTypeControllerDelegate> delegate_on_disk, - std::unique_ptr<syncer::ModelTypeControllerDelegate> delegate_in_memory, + std::unique_ptr<syncer::ModelTypeControllerDelegate> + delegate_for_full_sync_mode, + std::unique_ptr<syncer::ModelTypeControllerDelegate> + delegate_for_transport_mode, PrefService* pref_service, syncer::SyncService* sync_service) : ModelTypeController(type, - std::move(delegate_on_disk), - std::move(delegate_in_memory)), + std::move(delegate_for_full_sync_mode), + std::move(delegate_for_transport_mode)), pref_service_(pref_service), sync_service_(sync_service) { DCHECK(type == syncer::AUTOFILL_WALLET_DATA || diff --git a/chromium/components/autofill/core/browser/payments/autofill_wallet_model_type_controller.h b/chromium/components/autofill/core/browser/payments/autofill_wallet_model_type_controller.h index 6cc8addad3b..03d0312c094 100644 --- a/chromium/components/autofill/core/browser/payments/autofill_wallet_model_type_controller.h +++ b/chromium/components/autofill/core/browser/payments/autofill_wallet_model_type_controller.h @@ -29,13 +29,16 @@ class AutofillWalletModelTypeController : public syncer::ModelTypeController, // |sync_client| must outlive this object. AutofillWalletModelTypeController( syncer::ModelType type, - std::unique_ptr<syncer::ModelTypeControllerDelegate> delegate_on_disk, + std::unique_ptr<syncer::ModelTypeControllerDelegate> + delegate_for_full_sync_mode, PrefService* pref_service, syncer::SyncService* sync_service); AutofillWalletModelTypeController( syncer::ModelType type, - std::unique_ptr<syncer::ModelTypeControllerDelegate> delegate_on_disk, - std::unique_ptr<syncer::ModelTypeControllerDelegate> delegate_in_memory, + std::unique_ptr<syncer::ModelTypeControllerDelegate> + delegate_for_full_sync_mode, + std::unique_ptr<syncer::ModelTypeControllerDelegate> + delegate_for_transport_mode, PrefService* pref_service, syncer::SyncService* sync_service); ~AutofillWalletModelTypeController() override; diff --git a/chromium/components/autofill/core/browser/payments/credit_card_access_manager.cc b/chromium/components/autofill/core/browser/payments/credit_card_access_manager.cc index ab11f3c6a1b..196fd861e58 100644 --- a/chromium/components/autofill/core/browser/payments/credit_card_access_manager.cc +++ b/chromium/components/autofill/core/browser/payments/credit_card_access_manager.cc @@ -25,14 +25,20 @@ #include "components/autofill/core/browser/metrics/credit_card_form_event_logger.h" #include "components/autofill/core/browser/payments/payments_client.h" #include "components/autofill/core/browser/personal_data_manager.h" +#include "components/autofill/core/common/autofill_clock.h" +#include "components/autofill/core/common/autofill_tick_clock.h" #include "components/strings/grit/components_strings.h" #include "ui/base/l10n/l10n_util.h" +#if !defined(OS_IOS) +#include "components/autofill/core/browser/payments/fido_authentication_strike_database.h" +#endif + namespace autofill { namespace { // Timeout to wait for unmask details from Google Payments in milliseconds. -constexpr int64_t kUnmaskDetailsResponseTimeout = 1000; +constexpr int64_t kUnmaskDetailsResponseTimeoutMs = 1000; // Time to wait between multiple calls to GetUnmaskDetails(). constexpr int64_t kDelayForGetUnmaskDetails = 3 * 60 * 1000; // 3 min @@ -40,7 +46,7 @@ constexpr int64_t kDelayForGetUnmaskDetails = 3 * 60 * 1000; // 3 min bool WaitForEvent(base::WaitableEvent* event) { event->declare_only_used_while_idle(); return event->TimedWait( - base::TimeDelta::FromMilliseconds(kUnmaskDetailsResponseTimeout)); + base::TimeDelta::FromMilliseconds(kUnmaskDetailsResponseTimeoutMs)); } // Used with PostTaskWithDelay() to signal event after a timeout. @@ -71,7 +77,13 @@ CreditCardAccessManager::CreditCardAccessManager( base::WaitableEvent::ResetPolicy::AUTOMATIC, base::WaitableEvent::InitialState::NOT_SIGNALED), can_fetch_unmask_details_(base::WaitableEvent::ResetPolicy::AUTOMATIC, - base::WaitableEvent::InitialState::SIGNALED) {} + base::WaitableEvent::InitialState::SIGNALED) { +#if !defined(OS_IOS) + // This is to initialize StrikeDatabase is if it hasn't been already, so that + // its cache would be loaded and ready to use when the first CCAM is created. + client_->GetStrikeDatabase(); +#endif +} CreditCardAccessManager::~CreditCardAccessManager() {} @@ -199,20 +211,28 @@ void CreditCardAccessManager::GetUnmaskDetailsIfUserIsVerifiable( // If user is verifiable, then make preflight call to payments to fetch unmask // details, otherwise the only option is to perform CVC Auth, which does not // require any. Do nothing if request is already in progress. - if (is_user_verifiable_ && !unmask_details_request_in_progress_) { + if (is_user_verifiable_.value_or(false) && + !unmask_details_request_in_progress_) { unmask_details_request_in_progress_ = true; payments_client_->GetUnmaskDetails( base::BindOnce(&CreditCardAccessManager::OnDidGetUnmaskDetails, weak_ptr_factory_.GetWeakPtr()), personal_data_manager_->app_locale()); + preflight_call_timestamp_ = AutofillTickClock::NowTicks(); + AutofillMetrics::LogCardUnmaskPreflightCalled(); } } void CreditCardAccessManager::OnDidGetUnmaskDetails( AutofillClient::PaymentsRpcResult result, AutofillClient::UnmaskDetails& unmask_details) { + // Log latency for preflight call. + AutofillMetrics::LogCardUnmaskPreflightDuration( + AutofillTickClock::NowTicks() - preflight_call_timestamp_); + unmask_details_request_in_progress_ = false; - unmask_details_.offer_fido_opt_in = unmask_details.offer_fido_opt_in; + unmask_details_.offer_fido_opt_in = unmask_details.offer_fido_opt_in && + !payments_client_->is_off_the_record(); unmask_details_.unmask_auth_method = unmask_details.unmask_auth_method; unmask_details_.fido_request_options = std::move(unmask_details.fido_request_options); @@ -243,13 +263,28 @@ void CreditCardAccessManager::FetchCreditCard( const CreditCard* card, base::WeakPtr<Accessor> accessor, const base::TimeTicks& form_parsed_timestamp) { + // Return error if authentication is already in progress or card is nullptr. if (is_authentication_in_progress_ || !card) { accessor->OnCreditCardFetched(/*did_succeed=*/false, nullptr); return; } + // Latency metrics should only be logged if the user is verifiable and the + // flag is turned on. If flag is turned off, then |is_user_verifiable_| is not + // set. +#if !defined(OS_IOS) + bool should_log_latency_metrics = is_user_verifiable_.value_or(false); +#endif + // Return immediately if local card and log that unmask details were ignored. if (card->record_type() != CreditCard::MASKED_SERVER_CARD) { accessor->OnCreditCardFetched(/*did_succeed=*/true, card); +#if !defined(OS_IOS) + if (should_log_latency_metrics) { + AutofillMetrics::LogUserPerceivedLatencyOnCardSelection( + AutofillMetrics::PreflightCallEvent::kDidNotChooseMaskedCard, + GetOrCreateFIDOAuthenticator()->IsUserOptedIn()); + } +#endif return; } @@ -258,7 +293,32 @@ void CreditCardAccessManager::FetchCreditCard( form_parsed_timestamp_ = form_parsed_timestamp; is_authentication_in_progress_ = true; - if (AuthenticationRequiresUnmaskDetails()) { + bool get_unmask_details_returned = + ready_to_start_authentication_.IsSignaled(); + bool user_is_opted_in = AuthenticationRequiresUnmaskDetails(); + bool should_wait_to_authenticate = + user_is_opted_in && !get_unmask_details_returned; + + // Logging metrics. +#if !defined(OS_IOS) + if (should_log_latency_metrics) { + AutofillMetrics::LogUserPerceivedLatencyOnCardSelection( + get_unmask_details_returned + ? AutofillMetrics::PreflightCallEvent:: + kPreflightCallReturnedBeforeCardChosen + : AutofillMetrics::PreflightCallEvent:: + kCardChosenBeforePreflightCallReturned, + GetOrCreateFIDOAuthenticator()->IsUserOptedIn()); + } +#endif + +#if !defined(OS_ANDROID) && !defined(OS_IOS) + // On desktop, show the verify pending dialog for opted-in user. + if (user_is_opted_in) + ShowVerifyPendingDialog(); +#endif + + if (should_wait_to_authenticate) { // Wait for |ready_to_start_authentication_| to be signaled by // OnDidGetUnmaskDetails() or until timeout before calling Authenticate(). base::PostTaskAndReplyWithResult( @@ -267,37 +327,69 @@ void CreditCardAccessManager::FetchCreditCard( base::BindOnce(&CreditCardAccessManager::Authenticate, weak_ptr_factory_.GetWeakPtr())); } else { - Authenticate(); + Authenticate(get_unmask_details_returned); } } -void CreditCardAccessManager::FIDOAuthOptChange(bool opt_in, - base::Value creation_options) { +void CreditCardAccessManager::FIDOAuthOptChange(bool opt_in) { #if defined(OS_IOS) return; #else if (opt_in) { - GetOrCreateFIDOAuthenticator()->Register(std::move(creation_options)); + GetOrCreateFIDOAuthenticator()->ShowWebauthnOfferDialog( + /*card_authorization_token=*/std::string()); } else { GetOrCreateFIDOAuthenticator()->OptOut(); + GetOrCreateFIDOAuthenticator() + ->GetOrCreateFidoAuthenticationStrikeDatabase() + ->AddStrikes( + FidoAuthenticationStrikeDatabase::kStrikesToAddWhenUserOptsOut); } #endif } -void CreditCardAccessManager::Authenticate(bool did_get_unmask_details) { +void CreditCardAccessManager::OnSettingsPageFIDOAuthToggled(bool opt_in) { +#if defined(OS_IOS) + return; +#else + // TODO(crbug/949269): Add a rate limiter to counter spam clicking. + FIDOAuthOptChange(opt_in); +#endif +} + +void CreditCardAccessManager::Authenticate(bool get_unmask_details_returned) { // Reset now that we have started authentication. ready_to_start_authentication_.Reset(); unmask_details_request_in_progress_ = false; - // Do not use FIDO if card is not listed in unmask details, as each Card must - // be CVC authed at least once per device. - bool card_is_eligible_for_fido = - did_get_unmask_details && - unmask_details_.unmask_auth_method == - AutofillClient::UnmaskAuthMethod::FIDO && + bool fido_auth_suggested = + get_unmask_details_returned && unmask_details_.unmask_auth_method == + AutofillClient::UnmaskAuthMethod::FIDO; + + bool card_is_authorized_for_fido = + fido_auth_suggested && unmask_details_.fido_eligible_card_ids.find(card_->server_id()) != unmask_details_.fido_eligible_card_ids.end(); + // If FIDO authentication was suggested, but card is not in authorized list, + // must authenticate with CVC followed by FIDO in order to authorize this card + // for future FIDO use. + should_follow_up_cvc_with_fido_auth_ = + fido_auth_suggested && !card_is_authorized_for_fido; + + // Only use FIDO if card is authorized and not expired. + bool card_is_eligible_for_fido = + card_is_authorized_for_fido && !card_->IsExpired(AutofillClock::Now()); + + // If FIDO auth was suggested, logging which authentication method was + // actually used. + if (fido_auth_suggested && !card_->IsExpired(AutofillClock::Now())) { + AutofillMetrics::LogCardUnmaskTypeDecision( + card_is_eligible_for_fido + ? AutofillMetrics::CardUnmaskTypeDecisionMetric::kFidoOnly + : AutofillMetrics::CardUnmaskTypeDecisionMetric::kCvcThenFido); + } + if (card_is_eligible_for_fido) { #if defined(OS_IOS) NOTREACHED(); @@ -308,6 +400,11 @@ void CreditCardAccessManager::Authenticate(bool did_get_unmask_details) { std::move(unmask_details_.fido_request_options)); #endif } else { +#if !defined(OS_ANDROID) && !defined(OS_IOS) + // Close the verify pending dialog if it enters CVC authentication flow + // since the card unmask prompt will pop up. + client_->CloseVerifyPendingDialog(); +#endif GetOrCreateCVCAuthenticator()->Authenticate( card_, weak_ptr_factory_.GetWeakPtr(), personal_data_manager_, form_parsed_timestamp_); @@ -338,23 +435,48 @@ void CreditCardAccessManager::OnCVCAuthenticationComplete( response.cvc); can_fetch_unmask_details_.Signal(); - if (!response.did_succeed) + if (!response.did_succeed || response.card_authorization_token.empty()) return; #if defined(OS_ANDROID) - // Now that unmask flow is complete, on Android, if GetRealPan includes - // |creation_options|, completely hand over registration flow to - // CreditCardFIDOAuthenticator. + // Opt-in was already offered at this point for Android. + bool should_offer_fido_auth = false; +#elif !defined(OS_IOS) + bool should_offer_fido_auth = + unmask_details_.offer_fido_opt_in && + !GetOrCreateFIDOAuthenticator() + ->GetOrCreateFidoAuthenticationStrikeDatabase() + ->IsMaxStrikesLimitReached(); +#endif + +#if !defined(OS_IOS) + // Now that unmask flow is complete and form is filled, the remaining flows + // will be completely handed over to CreditCardFIDOAuthenticator. + // If the GetRealPan response includes |creation_options| or + // |request_options|, that means the user showed intention to opt-in while + // unmasking (this can only happen on Android) and must complete the challenge + // before successfully opting-in. If UnmaskDetails contained + // |request_options|, that means the user is already opted-into FIDO auth, and + // this is the first time use of a new card, and must complete the challenge + // to successfully authorize the card. Otherwise, if on desktop and eligible, + // show the dialog that offers opting-in to FIDO authentication in the future. if (response.creation_options.has_value()) { DCHECK(response.creation_options->is_dict()); GetOrCreateFIDOAuthenticator()->Register( - response.creation_options->Clone()); + response.card_authorization_token, response.creation_options->Clone()); + } else if (response.request_options.has_value()) { + DCHECK(response.request_options->is_dict()); + GetOrCreateFIDOAuthenticator()->Authorize( + response.card_authorization_token, response.request_options->Clone()); + } else if (should_offer_fido_auth) { + GetOrCreateFIDOAuthenticator()->ShowWebauthnOfferDialog( + response.card_authorization_token); + } else if (should_follow_up_cvc_with_fido_auth_) { + DCHECK(unmask_details_.fido_request_options.is_dict()); + GetOrCreateFIDOAuthenticator()->Authorize( + response.card_authorization_token, + std::move(unmask_details_.fido_request_options)); } -#elif !defined(OS_IOS) - // CreditCardFIDOAuthenticator does not exist on iOS. - // On desktop, prompts dialog to show the authentication offer. - if (unmask_details_.offer_fido_opt_in) - GetOrCreateFIDOAuthenticator()->ShowWebauthnOfferDialog(); #endif } @@ -362,6 +484,13 @@ void CreditCardAccessManager::OnCVCAuthenticationComplete( void CreditCardAccessManager::OnFIDOAuthenticationComplete( bool did_succeed, const CreditCard* card) { +#if !defined(OS_ANDROID) + // Close the verify pending dialog. If FIDO authentication succeeded, card is + // filled to the form, otherwise fall back to CVC authentication which does + // not need the verify pending dialog either. + client_->CloseVerifyPendingDialog(); +#endif + if (did_succeed) { is_authentication_in_progress_ = false; accessor_->OnCreditCardFetched(did_succeed, card); @@ -376,6 +505,10 @@ void CreditCardAccessManager::OnFIDOAuthenticationComplete( } #endif +bool CreditCardAccessManager::IsLocalCard(const CreditCard* card) { + return card && card->record_type() == CreditCard::LOCAL_CARD; +} + bool CreditCardAccessManager::AuthenticationRequiresUnmaskDetails() { #if defined(OS_IOS) return false; @@ -385,8 +518,18 @@ bool CreditCardAccessManager::AuthenticationRequiresUnmaskDetails() { #endif } -bool CreditCardAccessManager::IsLocalCard(const CreditCard* card) { - return card && card->record_type() == CreditCard::LOCAL_CARD; +#if !defined(OS_ANDROID) && !defined(OS_IOS) +void CreditCardAccessManager::ShowVerifyPendingDialog() { + client_->ShowVerifyPendingDialog( + base::BindOnce(&CreditCardAccessManager::OnDidCancelCardVerification, + weak_ptr_factory_.GetWeakPtr())); +} + +void CreditCardAccessManager::OnDidCancelCardVerification() { + payments_client_->CancelRequest(); + unmask_details_request_in_progress_ = false; + is_authentication_in_progress_ = false; } +#endif } // namespace autofill diff --git a/chromium/components/autofill/core/browser/payments/credit_card_access_manager.h b/chromium/components/autofill/core/browser/payments/credit_card_access_manager.h index 0307279777c..7c6868a2761 100644 --- a/chromium/components/autofill/core/browser/payments/credit_card_access_manager.h +++ b/chromium/components/autofill/core/browser/payments/credit_card_access_manager.h @@ -91,8 +91,11 @@ class CreditCardAccessManager : public CreditCardCVCAuthenticator::Requester, // If |opt_in| = true, opts the user into using FIDO authentication for card // unmasking. Otherwise, opts the user out. If |creation_options| is set, // WebAuthn registration prompt will be invoked to create a new credential. - void FIDOAuthOptChange(bool opt_in, - base::Value creation_options = base::Value()); + void FIDOAuthOptChange(bool opt_in); + + // Makes a call to FIDOAuthOptChange() with |opt_in|. + // TODO(crbug/949269): Add a rate limiter to counter spam clicking. + void OnSettingsPageFIDOAuthToggled(bool opt_in); CreditCardCVCAuthenticator* GetOrCreateCVCAuthenticator(); @@ -128,10 +131,12 @@ class CreditCardAccessManager : public CreditCardCVCAuthenticator::Requester, void OnDidGetUnmaskDetails(AutofillClient::PaymentsRpcResult result, AutofillClient::UnmaskDetails& unmask_details); - // Calls either CreditCardFIDOAuthenticator::Authenticate() or - // CreditCardCVCAuthenticator::Authenticate() depending on the response - // contained in |unmask_details_|. - void Authenticate(bool did_get_unmask_details = false); + // If OnDidGetUnmaskDetails() was invoked by PaymentsClient, then + // |get_unmask_details_returned| should be set to true. Based on the + // contents of |unmask_details_|, either FIDO authentication or CVC + // authentication will be prompted. If |get_unmask_details_returned| is false, + // then only CVC authentication will be prompted. + void Authenticate(bool get_unmask_details_returned = false); // CreditCardCVCAuthenticator::Requester: void OnCVCAuthenticationComplete( @@ -156,10 +161,25 @@ class CreditCardAccessManager : public CreditCardCVCAuthenticator::Requester, // immediately. bool AuthenticationRequiresUnmaskDetails(); +#if !defined(OS_ANDROID) && !defined(OS_IOS) + // After card verification starts, shows the verify pending dialog if WebAuthn + // is enabled, indicating some verification steps are in progress. + void ShowVerifyPendingDialog(); + + // The callback function invoked when the cancel button in the verify pending + // dialog is clicked. Will cancel the attempt to fetch unmask details. + void OnDidCancelCardVerification(); +#endif + // Is set to true only when waiting for the callback to // OnCVCAuthenticationComplete() to be executed. bool is_authentication_in_progress_ = false; + // Set to true if the card selected needs to be authenticated through CVC + // first, and then FIDO. This happens when a user is opted-in but has not + // previously authenticated this card with CVC on this device. + bool should_follow_up_cvc_with_fido_auth_ = false; + // The associated autofill driver. Weak reference. AutofillDriver* const driver_; @@ -178,6 +198,9 @@ class CreditCardAccessManager : public CreditCardCVCAuthenticator::Requester, // For logging metrics. May be NULL for tests. CreditCardFormEventLogger* form_event_logger_; + // Timestamp used for metrics. + base::TimeTicks preflight_call_timestamp_; + // Meant for histograms recorded in FullCardRequest. base::TimeTicks form_parsed_timestamp_; diff --git a/chromium/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc b/chromium/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc index f45ea4e58c5..c149dee68a7 100644 --- a/chromium/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc @@ -69,6 +69,7 @@ #include "url/gurl.h" #if !defined(OS_IOS) +#include "components/autofill/core/browser/payments/fido_authentication_strike_database.h" #include "components/autofill/core/browser/payments/test_credit_card_fido_authenticator.h" #endif @@ -126,13 +127,13 @@ class TestAccessor : public CreditCardAccessManager::Accessor { std::string NextYear() { base::Time::Exploded now; - base::Time::Now().LocalExplode(&now); + AutofillClock::Now().LocalExplode(&now); return base::NumberToString(now.year + 1); } std::string NextMonth() { base::Time::Exploded now; - base::Time::Now().LocalExplode(&now); + AutofillClock::Now().LocalExplode(&now); return base::NumberToString(now.month % 12 + 1); } @@ -165,6 +166,8 @@ class CreditCardAccessManagerTest : public testing::Test { autofill_client_.GetIdentityManager(), &personal_data_manager_); autofill_client_.set_test_payments_client( std::unique_ptr<payments::TestPaymentsClient>(payments_client_)); + autofill_client_.set_test_strike_database( + std::make_unique<TestStrikeDatabase>()); credit_card_access_manager_ = std::make_unique<CreditCardAccessManager>( autofill_driver_.get(), &autofill_client_, &personal_data_manager_, nullptr); @@ -188,6 +191,15 @@ class CreditCardAccessManagerTest : public testing::Test { return credit_card_access_manager_->is_authentication_in_progress(); } + void ResetFetchCreditCard() { + // Resets all variables related to credit card fetching. + credit_card_access_manager_->is_authentication_in_progress_ = false; + credit_card_access_manager_->can_fetch_unmask_details_.Signal(); + credit_card_access_manager_->is_user_verifiable_ = base::nullopt; + } + + void ClearCards() { personal_data_manager_.ClearCreditCards(); } + void CreateLocalCard(std::string guid, std::string number = std::string()) { CreditCard local_card = CreditCard(); test::SetCreditCardInfo(&local_card, "Elvis Presley", number.c_str(), @@ -195,7 +207,6 @@ class CreditCardAccessManagerTest : public testing::Test { local_card.set_guid(guid); local_card.set_record_type(CreditCard::LOCAL_CARD); - personal_data_manager_.ClearCreditCards(); personal_data_manager_.AddCreditCard(local_card); } @@ -207,7 +218,6 @@ class CreditCardAccessManagerTest : public testing::Test { masked_server_card.set_guid(guid); masked_server_card.set_record_type(CreditCard::MASKED_SERVER_CARD); - personal_data_manager_.ClearCreditCards(); personal_data_manager_.AddServerCreditCard(masked_server_card); } @@ -218,22 +228,27 @@ class CreditCardAccessManagerTest : public testing::Test { // Returns true if full card request was sent from CVC auth. bool GetRealPanForCVCAuth(AutofillClient::PaymentsRpcResult result, const std::string& real_pan, - bool fido_opt_in = false) { + bool fido_opt_in = false, + bool follow_with_fido_auth = false) { payments::FullCardRequest* full_card_request = GetCVCAuthenticator()->full_card_request_.get(); if (!full_card_request) return false; + // Mock user response. + payments::FullCardRequest::UserProvidedUnmaskDetails details; + details.cvc = base::ASCIIToUTF16("123"); + full_card_request->OnUnmaskPromptAccepted(details); + payments::PaymentsClient::UnmaskResponseDetails response; #if !defined(OS_IOS) + response.card_authorization_token = "dummy_card_authorization_token"; if (fido_opt_in) { - response.fido_creation_options = - base::Value(base::Value::Type::DICTIONARY); - response.fido_creation_options->SetKey("relying_party_id", - base::Value(kGooglePaymentsRpid)); - response.fido_creation_options->SetKey("challenge", - base::Value(kTestChallenge)); + response.fido_creation_options = GetTestCreationOptions(); + } + if (follow_with_fido_auth) { + response.fido_request_options = GetTestRequestOptions(); } #endif full_card_request->OnDidGetRealPan(result, @@ -242,7 +257,43 @@ class CreditCardAccessManagerTest : public testing::Test { } #if !defined(OS_IOS) + void ClearStrikes() { + return GetFIDOAuthenticator() + ->GetOrCreateFidoAuthenticationStrikeDatabase() + ->ClearAllStrikes(); + } + + int GetStrikes() { + return GetFIDOAuthenticator() + ->GetOrCreateFidoAuthenticationStrikeDatabase() + ->GetStrikes(); + } + + base::Value GetTestRequestOptions() { + base::Value request_options = base::Value(base::Value::Type::DICTIONARY); + request_options.SetKey("challenge", base::Value(kTestChallenge)); + request_options.SetKey("relying_party_id", + base::Value(kGooglePaymentsRpid)); + + base::Value key_info(base::Value::Type::DICTIONARY); + key_info.SetKey("credential_id", base::Value(kCredentialId)); + request_options.SetKey("key_info", base::Value(base::Value::Type::LIST)); + request_options.FindKeyOfType("key_info", base::Value::Type::LIST) + ->GetList() + .push_back(std::move(key_info)); + return request_options; + } + + base::Value GetTestCreationOptions() { + base::Value creation_options = base::Value(base::Value::Type::DICTIONARY); + creation_options.SetKey("challenge", base::Value(kTestChallenge)); + creation_options.SetKey("relying_party_id", + base::Value(kGooglePaymentsRpid)); + return creation_options; + } + void SetUserOptedIn(bool user_is_opted_in) { + scoped_feature_list_.Reset(); scoped_feature_list_.InitAndEnableFeature( features::kAutofillCreditCardAuthentication); ::autofill::prefs::SetCreditCardFIDOAuthEnabled(autofill_client_.GetPrefs(), @@ -267,9 +318,22 @@ class CreditCardAccessManagerTest : public testing::Test { // Mocks an OptChange response from Payments Client. void OptChange(AutofillClient::PaymentsRpcResult result, bool user_is_opted_in, - base::Value creation_options = base::Value()) { - GetFIDOAuthenticator()->OnDidGetOptChangeResult( - result, user_is_opted_in, std::move(creation_options)); + bool include_creation_options = false, + bool include_request_options = false) { + payments::PaymentsClient::OptChangeResponseDetails response; + response.user_is_opted_in = user_is_opted_in; + if (include_creation_options) { + response.fido_creation_options = GetTestCreationOptions(); + } + if (include_request_options) { + response.fido_request_options = GetTestRequestOptions(); + } + GetFIDOAuthenticator()->OnDidGetOptChangeResult(result, response); + } + + // Mocks user response for the offer dialog. + void AcceptWebauthnOfferDialog(bool did_accept) { + GetFIDOAuthenticator()->OnWebauthnOfferDialogUserResponse(did_accept); } TestCreditCardFIDOAuthenticator* GetFIDOAuthenticator() { @@ -456,10 +520,87 @@ TEST_F(CreditCardAccessManagerTest, FetchServerCardCVCTryAgainFailure) { EXPECT_EQ(ASCIIToUTF16(kTestNumber), accessor_->number()); } +// Ensures that CardUnmaskPreflightCalled metrics are logged correctly. +TEST_F(CreditCardAccessManagerTest, CardUnmaskPreflightCalledMetric) { + std::string preflight_call_metric = + "Autofill.BetterAuth.CardUnmaskPreflightCalled"; + std::string preflight_latency_metric = + "Autofill.BetterAuth.CardUnmaskPreflightDuration"; + + { + // Create local card and set user as eligible for FIDO auth. + base::HistogramTester histogram_tester; + ClearCards(); + CreateLocalCard(kTestGUID, kTestNumber); +#if !defined(OS_IOS) + GetFIDOAuthenticator()->SetUserVerifiable(true); +#endif + ResetFetchCreditCard(); + + credit_card_access_manager_->PrepareToFetchCreditCard(); + InvokeUnmaskDetailsTimeout(); + WaitForCallbacks(); + + // If only local cards are available, then no preflight call is made. + histogram_tester.ExpectTotalCount(preflight_call_metric, 0); + histogram_tester.ExpectTotalCount(preflight_latency_metric, 0); + } + + { + // Create server card and set user as ineligible for FIDO auth. + base::HistogramTester histogram_tester; + ClearCards(); + CreateServerCard(kTestGUID, kTestNumber); +#if !defined(OS_IOS) + GetFIDOAuthenticator()->SetUserVerifiable(false); +#endif + ResetFetchCreditCard(); + + credit_card_access_manager_->PrepareToFetchCreditCard(); + InvokeUnmaskDetailsTimeout(); + WaitForCallbacks(); + + // If user is not verifiable, then no preflight call is made. + histogram_tester.ExpectTotalCount(preflight_call_metric, 0); + histogram_tester.ExpectTotalCount(preflight_latency_metric, 0); + } + + { + // Create server card and set user as eligible for FIDO auth. + base::HistogramTester histogram_tester; + ClearCards(); + CreateServerCard(kTestGUID, kTestNumber); +#if !defined(OS_IOS) + GetFIDOAuthenticator()->SetUserVerifiable(true); +#endif + ResetFetchCreditCard(); + + credit_card_access_manager_->PrepareToFetchCreditCard(); + InvokeUnmaskDetailsTimeout(); + WaitForCallbacks(); + + // Preflight call is made only if a server card is available and the user is + // eligible for FIDO authentication, except on iOS. +#if defined(OS_IOS) + histogram_tester.ExpectTotalCount(preflight_call_metric, 0); + histogram_tester.ExpectTotalCount(preflight_latency_metric, 0); +#else + histogram_tester.ExpectTotalCount(preflight_call_metric, 1); + histogram_tester.ExpectTotalCount(preflight_latency_metric, 1); +#endif + } +} + #if !defined(OS_IOS) // Ensures that FetchCreditCard() returns the full PAN upon a successful // WebAuthn verification and response from payments. TEST_F(CreditCardAccessManagerTest, FetchServerCardFIDOSuccess) { + base::HistogramTester histogram_tester; + std::string unmask_decision_histogram_name = + "Autofill.BetterAuth.CardUnmaskTypeDecision"; + std::string webauthn_result_histogram_name = + "Autofill.BetterAuth.WebauthnResult.ImmediateAuthentication"; + CreateServerCard(kTestGUID, kTestNumber); CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID); GetFIDOAuthenticator()->SetUserVerifiable(true); @@ -484,10 +625,26 @@ TEST_F(CreditCardAccessManagerTest, FetchServerCardFIDOSuccess) { EXPECT_EQ(kCredentialId, BytesToBase64(GetFIDOAuthenticator()->GetCredentialId())); EXPECT_EQ(ASCIIToUTF16(kTestNumber), accessor_->number()); + + histogram_tester.ExpectUniqueSample( + unmask_decision_histogram_name, + AutofillMetrics::CardUnmaskTypeDecisionMetric::kFidoOnly, 1); + histogram_tester.ExpectUniqueSample( + webauthn_result_histogram_name, + AutofillMetrics::WebauthnResultMetric::kSuccess, 1); + histogram_tester.ExpectTotalCount( + "Autofill.BetterAuth.CardUnmaskDuration.Fido", 1); + histogram_tester.ExpectTotalCount( + "Autofill.BetterAuth.CardUnmaskDuration.Fido.Success", 1); } // Ensures that CVC prompt is invoked after WebAuthn fails. -TEST_F(CreditCardAccessManagerTest, FetchServerCardFIDOFailureCVCFallback) { +TEST_F(CreditCardAccessManagerTest, + FetchServerCardFIDOVerificationFailureCVCFallback) { + base::HistogramTester histogram_tester; + std::string histogram_name = + "Autofill.BetterAuth.WebauthnResult.ImmediateAuthentication"; + CreateServerCard(kTestGUID, kTestNumber); CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID); GetFIDOAuthenticator()->SetUserVerifiable(true); @@ -515,6 +672,55 @@ TEST_F(CreditCardAccessManagerTest, FetchServerCardFIDOFailureCVCFallback) { EXPECT_TRUE(GetRealPanForCVCAuth(AutofillClient::SUCCESS, kTestNumber)); EXPECT_TRUE(accessor_->did_succeed()); EXPECT_EQ(ASCIIToUTF16(kTestNumber), accessor_->number()); + + histogram_tester.ExpectUniqueSample( + histogram_name, AutofillMetrics::WebauthnResultMetric::kNotAllowedError, + 1); +} + +// Ensures that CVC prompt is invoked after payments returns an error from +// GetRealPan via FIDO. +TEST_F(CreditCardAccessManagerTest, + FetchServerCardFIDOServerFailureCVCFallback) { + base::HistogramTester histogram_tester; + std::string histogram_name = + "Autofill.BetterAuth.WebauthnResult.ImmediateAuthentication"; + + CreateServerCard(kTestGUID, kTestNumber); + CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID); + GetFIDOAuthenticator()->SetUserVerifiable(true); + SetUserOptedIn(true); + payments_client_->AddFidoEligibleCard(card->server_id(), kCredentialId, + kGooglePaymentsRpid); + + credit_card_access_manager_->PrepareToFetchCreditCard(); + WaitForCallbacks(); + + credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr()); + WaitForCallbacks(); + + // FIDO Failure. + EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::AUTHENTICATION_FLOW, + GetFIDOAuthenticator()->current_flow()); + TestCreditCardFIDOAuthenticator::GetAssertion(GetFIDOAuthenticator(), + /*did_succeed=*/true); + EXPECT_TRUE( + GetRealPanForFIDOAuth(AutofillClient::PERMANENT_FAILURE, kTestNumber)); + EXPECT_FALSE(accessor_->did_succeed()); + + // Followed by a fallback to CVC. + EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::NONE_FLOW, + GetFIDOAuthenticator()->current_flow()); + EXPECT_TRUE(GetRealPanForCVCAuth(AutofillClient::SUCCESS, kTestNumber)); + EXPECT_TRUE(accessor_->did_succeed()); + EXPECT_EQ(ASCIIToUTF16(kTestNumber), accessor_->number()); + + histogram_tester.ExpectUniqueSample( + histogram_name, AutofillMetrics::WebauthnResultMetric::kSuccess, 1); + histogram_tester.ExpectTotalCount( + "Autofill.BetterAuth.CardUnmaskDuration.Fido", 1); + histogram_tester.ExpectTotalCount( + "Autofill.BetterAuth.CardUnmaskDuration.Fido.Failure", 1); } // Ensures WebAuthn call is not made if Request Options is missing a Credential @@ -561,14 +767,161 @@ TEST_F(CreditCardAccessManagerTest, FetchServerCardFIDOTimeoutCVCFallback) { EXPECT_TRUE(accessor_->did_succeed()); EXPECT_EQ(ASCIIToUTF16(kTestNumber), accessor_->number()); } -#endif -// TODO(crbug.com/991037): Add tests for desktop separately after the -// WebauthnOfferDelegate functions are implemented since the flows are different -// on desktop and Android. +// Ensures that FetchCreditCard() returns the full PAN upon a successful +// WebAuthn verification and response from payments. +TEST_F(CreditCardAccessManagerTest, + Metrics_LoggingExistenceOfUserPerceivedLatency) { + // Setting up a FIDO-enabled user with a local card and a server card. + std::string server_guid = "00000000-0000-0000-0000-000000000001"; + std::string local_guid = "00000000-0000-0000-0000-000000000003"; + CreateServerCard(server_guid, "4594299181086168"); + CreateLocalCard(local_guid, "4409763681177079"); + CreditCard* server_card = + credit_card_access_manager_->GetCreditCard(server_guid); + CreditCard* local_card = + credit_card_access_manager_->GetCreditCard(local_guid); + GetFIDOAuthenticator()->SetUserVerifiable(true); + + for (bool user_is_opted_in : {true, false}) { + std::string histogram_name = + "Autofill.BetterAuth.UserPerceivedLatencyOnCardSelection."; + histogram_name += user_is_opted_in ? "OptedIn" : "OptedOut"; + SetUserOptedIn(user_is_opted_in); + + { + // Preflight call ignored because local card was chosen. + base::HistogramTester histogram_tester; + + ResetFetchCreditCard(); + credit_card_access_manager_->PrepareToFetchCreditCard(); + WaitForCallbacks(); + + credit_card_access_manager_->FetchCreditCard(local_card, + accessor_->GetWeakPtr()); + WaitForCallbacks(); + + histogram_tester.ExpectUniqueSample( + histogram_name, + AutofillMetrics::PreflightCallEvent::kDidNotChooseMaskedCard, 1); + } + + { + // Preflight call returned after card was chosen. + base::HistogramTester histogram_tester; + payments_client_->ShouldReturnUnmaskDetailsImmediately(false); + + ResetFetchCreditCard(); + credit_card_access_manager_->PrepareToFetchCreditCard(); + credit_card_access_manager_->FetchCreditCard(server_card, + accessor_->GetWeakPtr()); + WaitForCallbacks(); + + histogram_tester.ExpectUniqueSample( + histogram_name, + AutofillMetrics::PreflightCallEvent:: + kCardChosenBeforePreflightCallReturned, + 1); + } + + { + // Preflight call returned before card was chosen. + base::HistogramTester histogram_tester; + // This is important because CreditCardFIDOAuthenticator will update the + // opted-in pref according to GetDetailsForGetRealPan response. + payments_client_->AllowFidoRegistration(!user_is_opted_in); + + ResetFetchCreditCard(); + credit_card_access_manager_->PrepareToFetchCreditCard(); + WaitForCallbacks(); + + credit_card_access_manager_->FetchCreditCard(server_card, + accessor_->GetWeakPtr()); + WaitForCallbacks(); + + histogram_tester.ExpectUniqueSample( + histogram_name, + AutofillMetrics::PreflightCallEvent:: + kPreflightCallReturnedBeforeCardChosen, + 1); + } + } +} + +// Ensures that use of new card invokes authorization flow when user is +// opted-in. +TEST_F(CreditCardAccessManagerTest, FIDONewCardAuthorization) { + base::HistogramTester histogram_tester; + std::string unmask_decision_histogram_name = + "Autofill.BetterAuth.CardUnmaskTypeDecision"; + std::string webauthn_result_histogram_name = + "Autofill.BetterAuth.WebauthnResult.AuthenticationAfterCVC"; + + CreateServerCard(kTestGUID, kTestNumber); + CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID); + // Opt the user in, but don't include the card above. + std::string other_server_id = "00000000-0000-0000-0000-000000000034"; + payments_client_->AddFidoEligibleCard(other_server_id, kCredentialId, + kGooglePaymentsRpid); + GetFIDOAuthenticator()->SetUserVerifiable(true); + SetUserOptedIn(true); + + credit_card_access_manager_->PrepareToFetchCreditCard(); + WaitForCallbacks(); + + credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr()); + InvokeUnmaskDetailsTimeout(); + WaitForCallbacks(); + + EXPECT_TRUE(GetRealPanForCVCAuth(AutofillClient::SUCCESS, kTestNumber, + /*fido_opt_in=*/false, + /*follow_with_fido_auth=*/true)); + + // Mock user response and OptChange payments call. + EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::FOLLOWUP_AFTER_CVC_AUTH_FLOW, + GetFIDOAuthenticator()->current_flow()); + TestCreditCardFIDOAuthenticator::GetAssertion(GetFIDOAuthenticator(), + /*did_succeed=*/true); + OptChange(AutofillClient::SUCCESS, true); + + histogram_tester.ExpectUniqueSample( + unmask_decision_histogram_name, + AutofillMetrics::CardUnmaskTypeDecisionMetric::kCvcThenFido, 1); + histogram_tester.ExpectUniqueSample( + webauthn_result_histogram_name, + AutofillMetrics::WebauthnResultMetric::kSuccess, 1); +} + +// Ensures expired cards always invoke a CVC prompt instead of WebAuthn. +TEST_F(CreditCardAccessManagerTest, FetchExpiredServerCardInvokesCvcPrompt) { + // Creating an expired server card and opting the user in with authorized + // card. + CreateServerCard(kTestGUID, kTestNumber); + CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID); + card->SetExpirationYearFromString(base::UTF8ToUTF16("2010")); + GetFIDOAuthenticator()->SetUserVerifiable(true); + SetUserOptedIn(true); + payments_client_->AddFidoEligibleCard(card->server_id(), kCredentialId, + kGooglePaymentsRpid); + + credit_card_access_manager_->PrepareToFetchCreditCard(); + WaitForCallbacks(); + + credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr()); + WaitForCallbacks(); + + // Expect CVC prompt to be invoked. + EXPECT_TRUE(GetRealPanForCVCAuth(AutofillClient::SUCCESS, kTestNumber)); + EXPECT_EQ(ASCIIToUTF16(kTestNumber), accessor_->number()); +} + #if defined(OS_ANDROID) // Ensures that the WebAuthn enrollment prompt is invoked after user opts in. -TEST_F(CreditCardAccessManagerTest, FIDOEnrollmentSuccess) { +TEST_F(CreditCardAccessManagerTest, FIDOEnrollmentSuccess_Android) { + base::HistogramTester histogram_tester; + std::string histogram_name = + "Autofill.BetterAuth.WebauthnResult.CheckoutOptIn"; + CreateServerCard(kTestGUID, kTestNumber); CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID); GetFIDOAuthenticator()->SetUserVerifiable(true); @@ -593,10 +946,17 @@ TEST_F(CreditCardAccessManagerTest, FIDOEnrollmentSuccess) { EXPECT_EQ(kTestChallenge, BytesToBase64(GetFIDOAuthenticator()->GetChallenge())); EXPECT_TRUE(GetFIDOAuthenticator()->IsUserOptedIn()); + + histogram_tester.ExpectUniqueSample( + histogram_name, AutofillMetrics::WebauthnResultMetric::kSuccess, 1); } // Ensures that the failed user verification disallows enrollment. TEST_F(CreditCardAccessManagerTest, FIDOEnrollmentUserVerificationFailure) { + base::HistogramTester histogram_tester; + std::string histogram_name = + "Autofill.BetterAuth.WebauthnResult.CheckoutOptIn"; + CreateServerCard(kTestGUID, kTestNumber); CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID); GetFIDOAuthenticator()->SetUserVerifiable(true); @@ -614,6 +974,10 @@ TEST_F(CreditCardAccessManagerTest, FIDOEnrollmentUserVerificationFailure) { /*did_succeed=*/false); EXPECT_FALSE(GetFIDOAuthenticator()->IsUserOptedIn()); + + histogram_tester.ExpectUniqueSample( + histogram_name, AutofillMetrics::WebauthnResultMetric::kNotAllowedError, + 1); } // Ensures that enrollment does not happen if the server returns a failure. @@ -637,7 +1001,304 @@ TEST_F(CreditCardAccessManagerTest, FIDOEnrollmentServerFailure) { EXPECT_FALSE(GetFIDOAuthenticator()->IsUserOptedIn()); } -#endif + +#else // defined(OS_ANDROID) +// Ensures that the WebAuthn enrollment prompt is invoked after user opts in. In +// this case, the user is not yet enrolled server-side, and thus receives +// |creation_options|. +TEST_F(CreditCardAccessManagerTest, + FIDOEnrollmentSuccess_CreationOptions_Desktop) { + base::HistogramTester histogram_tester; + std::string webauthn_result_histogram_name = + "Autofill.BetterAuth.WebauthnResult.CheckoutOptIn"; + std::string opt_in_histogram_name = + "Autofill.BetterAuth.OptInCalled.FromCheckoutFlow"; + std::string promo_shown_histogram_name = + "Autofill.BetterAuth.OptInPromoShown.FromCheckoutFlow"; + std::string promo_user_decision_histogram_name = + "Autofill.BetterAuth.OptInPromoUserDecision.FromCheckoutFlow"; + + ClearStrikes(); + CreateServerCard(kTestGUID, kTestNumber); + CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID); + GetFIDOAuthenticator()->SetUserVerifiable(true); + SetUserOptedIn(false); + payments_client_->AllowFidoRegistration(true); + + credit_card_access_manager_->PrepareToFetchCreditCard(); + credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr()); + WaitForCallbacks(); + + // Mock user and payments response. + EXPECT_TRUE(GetRealPanForCVCAuth(AutofillClient::SUCCESS, kTestNumber, + /*fido_opt_in=*/false)); + AcceptWebauthnOfferDialog(/*did_accept=*/true); + + OptChange(AutofillClient::SUCCESS, /*user_is_opted_in=*/false, + /*include_creation_options=*/true); + + // Mock user response and OptChange payments call. + EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::OPT_IN_WITH_CHALLENGE_FLOW, + GetFIDOAuthenticator()->current_flow()); + TestCreditCardFIDOAuthenticator::MakeCredential(GetFIDOAuthenticator(), + /*did_succeed=*/true); + OptChange(AutofillClient::SUCCESS, /*user_is_opted_in=*/true); + + EXPECT_EQ(kGooglePaymentsRpid, GetFIDOAuthenticator()->GetRelyingPartyId()); + EXPECT_EQ(kTestChallenge, + BytesToBase64(GetFIDOAuthenticator()->GetChallenge())); + EXPECT_TRUE(GetFIDOAuthenticator()->IsUserOptedIn()); + EXPECT_EQ(0, GetStrikes()); + histogram_tester.ExpectUniqueSample( + webauthn_result_histogram_name, + AutofillMetrics::WebauthnResultMetric::kSuccess, 1); + histogram_tester.ExpectTotalCount(opt_in_histogram_name, 2); + histogram_tester.ExpectBucketCount( + opt_in_histogram_name, + AutofillMetrics::WebauthnOptInParameters::kFetchingChallenge, 1); + histogram_tester.ExpectBucketCount( + opt_in_histogram_name, + AutofillMetrics::WebauthnOptInParameters::kWithCreationChallenge, 1); + histogram_tester.ExpectTotalCount(promo_shown_histogram_name, 1); + histogram_tester.ExpectUniqueSample( + promo_user_decision_histogram_name, + AutofillMetrics::WebauthnOptInPromoUserDecisionMetric::kAccepted, 1); +} + +// Ensures that the correct number of strikes are added when the user declines +// the WebAuthn offer. +TEST_F(CreditCardAccessManagerTest, FIDOEnrollment_OfferDeclined_Desktop) { + base::HistogramTester histogram_tester; + std::string promo_shown_histogram_name = + "Autofill.BetterAuth.OptInPromoShown.FromCheckoutFlow"; + std::string promo_user_decision_histogram_name = + "Autofill.BetterAuth.OptInPromoUserDecision.FromCheckoutFlow"; + + ClearStrikes(); + CreateServerCard(kTestGUID, kTestNumber); + CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID); + GetFIDOAuthenticator()->SetUserVerifiable(true); + SetUserOptedIn(false); + payments_client_->AllowFidoRegistration(true); + + credit_card_access_manager_->PrepareToFetchCreditCard(); + credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr()); + WaitForCallbacks(); + + // Mock user and payments response. + EXPECT_TRUE(GetRealPanForCVCAuth(AutofillClient::SUCCESS, kTestNumber, + /*fido_opt_in=*/false)); + AcceptWebauthnOfferDialog(/*did_accept=*/false); + EXPECT_EQ( + FidoAuthenticationStrikeDatabase::kStrikesToAddWhenOptInOfferDeclined, + GetStrikes()); + histogram_tester.ExpectTotalCount(promo_shown_histogram_name, 1); + histogram_tester.ExpectUniqueSample( + promo_user_decision_histogram_name, + AutofillMetrics::WebauthnOptInPromoUserDecisionMetric:: + kDeclinedImmediately, + 1); +} + +// Ensures that the correct number of strikes are added when the user declines +// the WebAuthn offer. +TEST_F(CreditCardAccessManagerTest, + FIDOEnrollment_OfferDeclinedAfterAccepting_Desktop) { + base::HistogramTester histogram_tester; + std::string promo_shown_histogram_name = + "Autofill.BetterAuth.OptInPromoShown.FromCheckoutFlow"; + std::string promo_user_decision_histogram_name = + "Autofill.BetterAuth.OptInPromoUserDecision.FromCheckoutFlow"; + + ClearStrikes(); + CreateServerCard(kTestGUID, kTestNumber); + CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID); + GetFIDOAuthenticator()->SetUserVerifiable(true); + SetUserOptedIn(false); + payments_client_->AllowFidoRegistration(true); + + credit_card_access_manager_->PrepareToFetchCreditCard(); + credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr()); + WaitForCallbacks(); + + // Mock user and payments response. + EXPECT_TRUE(GetRealPanForCVCAuth(AutofillClient::SUCCESS, kTestNumber, + /*fido_opt_in=*/false)); + AcceptWebauthnOfferDialog(/*did_accept=*/true); + AcceptWebauthnOfferDialog(/*did_accept=*/false); + EXPECT_EQ( + FidoAuthenticationStrikeDatabase::kStrikesToAddWhenOptInOfferDeclined, + GetStrikes()); + histogram_tester.ExpectTotalCount(promo_shown_histogram_name, 1); + histogram_tester.ExpectUniqueSample( + promo_user_decision_histogram_name, + AutofillMetrics::WebauthnOptInPromoUserDecisionMetric:: + kDeclinedAfterAccepting, + 1); +} + +// Ensures that the correct number of strikes are added when the user fails to +// complete user-verification for an opt-in attempt. +TEST_F(CreditCardAccessManagerTest, + FIDOEnrollment_UserVerificationFailed_Desktop) { + base::HistogramTester histogram_tester; + std::string webauthn_result_histogram_name = + "Autofill.BetterAuth.WebauthnResult.CheckoutOptIn"; + std::string opt_in_histogram_name = + "Autofill.BetterAuth.OptInCalled.FromCheckoutFlow"; + + ClearStrikes(); + CreateServerCard(kTestGUID, kTestNumber); + CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID); + GetFIDOAuthenticator()->SetUserVerifiable(true); + SetUserOptedIn(false); + payments_client_->AllowFidoRegistration(true); + + credit_card_access_manager_->PrepareToFetchCreditCard(); + credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr()); + InvokeUnmaskDetailsTimeout(); + WaitForCallbacks(); + + // Mock user and payments response. + EXPECT_TRUE(GetRealPanForCVCAuth(AutofillClient::SUCCESS, kTestNumber, + /*fido_opt_in=*/false)); + WaitForCallbacks(); + AcceptWebauthnOfferDialog(/*did_accept=*/true); + + OptChange(AutofillClient::SUCCESS, /*user_is_opted_in=*/false, + /*include_creation_options=*/true); + + // Mock user response. + TestCreditCardFIDOAuthenticator::MakeCredential(GetFIDOAuthenticator(), + /*did_succeed=*/false); + EXPECT_EQ(FidoAuthenticationStrikeDatabase:: + kStrikesToAddWhenUserVerificationFailsOnOptInAttempt, + GetStrikes()); + histogram_tester.ExpectUniqueSample( + webauthn_result_histogram_name, + AutofillMetrics::WebauthnResultMetric::kNotAllowedError, 1); + histogram_tester.ExpectUniqueSample( + opt_in_histogram_name, + AutofillMetrics::WebauthnOptInParameters::kFetchingChallenge, 1); +} + +// Ensures that the WebAuthn enrollment prompt is invoked after user opts in. In +// this case, the user is already enrolled server-side, and thus receives +// |request_options|. +TEST_F(CreditCardAccessManagerTest, + FIDOEnrollmentSuccess_RequestOptions_Desktop) { + base::HistogramTester histogram_tester; + std::string webauthn_result_histogram_name = + "Autofill.BetterAuth.WebauthnResult.CheckoutOptIn"; + std::string opt_in_histogram_name = + "Autofill.BetterAuth.OptInCalled.FromCheckoutFlow"; + + CreateServerCard(kTestGUID, kTestNumber); + CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID); + GetFIDOAuthenticator()->SetUserVerifiable(true); + SetUserOptedIn(false); + payments_client_->AllowFidoRegistration(true); + + credit_card_access_manager_->PrepareToFetchCreditCard(); + credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr()); + WaitForCallbacks(); + + // Mock user and payments response. + EXPECT_TRUE(GetRealPanForCVCAuth(AutofillClient::SUCCESS, kTestNumber, + /*fido_opt_in=*/false)); + WaitForCallbacks(); + AcceptWebauthnOfferDialog(/*did_accept=*/true); + + OptChange(AutofillClient::SUCCESS, /*user_is_opted_in=*/false, + /*include_creation_options=*/false, + /*include_request_options=*/true); + + // Mock user response and OptChange payments call. + EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::OPT_IN_WITH_CHALLENGE_FLOW, + GetFIDOAuthenticator()->current_flow()); + TestCreditCardFIDOAuthenticator::GetAssertion(GetFIDOAuthenticator(), + /*did_succeed=*/true); + OptChange(AutofillClient::SUCCESS, /*user_is_opted_in=*/true); + + EXPECT_EQ(kGooglePaymentsRpid, GetFIDOAuthenticator()->GetRelyingPartyId()); + EXPECT_EQ(kTestChallenge, + BytesToBase64(GetFIDOAuthenticator()->GetChallenge())); + EXPECT_TRUE(GetFIDOAuthenticator()->IsUserOptedIn()); + + histogram_tester.ExpectUniqueSample( + webauthn_result_histogram_name, + AutofillMetrics::WebauthnResultMetric::kSuccess, 1); + histogram_tester.ExpectTotalCount(opt_in_histogram_name, 2); + histogram_tester.ExpectBucketCount( + opt_in_histogram_name, + AutofillMetrics::WebauthnOptInParameters::kFetchingChallenge, 1); + histogram_tester.ExpectBucketCount( + opt_in_histogram_name, + AutofillMetrics::WebauthnOptInParameters::kWithRequestChallenge, 1); +} + +// Ensures WebAuthn result is logged correctly for a settings page opt-in. +TEST_F(CreditCardAccessManagerTest, SettingsPage_FIDOEnrollment) { + base::HistogramTester histogram_tester; + std::string webauthn_histogram_name = + "Autofill.BetterAuth.WebauthnResult.SettingsPageOptIn"; + std::string opt_in_histogram_name = + "Autofill.BetterAuth.OptInCalled.FromSettingsPage"; + std::string promo_shown_histogram_name = + "Autofill.BetterAuth.OptInPromoShown.FromSettingsPage"; + std::string promo_user_decision_histogram_name = + "Autofill.BetterAuth.OptInPromoUserDecision.FromSettingsPage"; + + GetFIDOAuthenticator()->SetUserVerifiable(true); + + for (bool did_succeed : {false, true}) { + SetUserOptedIn(false); + credit_card_access_manager_->OnSettingsPageFIDOAuthToggled(true); + + // Mock user and payments response. + AcceptWebauthnOfferDialog(/*did_accept=*/true); + OptChange(AutofillClient::SUCCESS, /*user_is_opted_in=*/false, + /*include_creation_options=*/true); + // Mock user response and payments response. + TestCreditCardFIDOAuthenticator::MakeCredential(GetFIDOAuthenticator(), + did_succeed); + + histogram_tester.ExpectBucketCount( + webauthn_histogram_name, + did_succeed ? AutofillMetrics::WebauthnResultMetric::kSuccess + : AutofillMetrics::WebauthnResultMetric::kNotAllowedError, + 1); + } + + histogram_tester.ExpectTotalCount(webauthn_histogram_name, 2); + histogram_tester.ExpectTotalCount(opt_in_histogram_name, 3); + histogram_tester.ExpectBucketCount( + opt_in_histogram_name, + AutofillMetrics::WebauthnOptInParameters::kFetchingChallenge, 2); + histogram_tester.ExpectBucketCount( + opt_in_histogram_name, + AutofillMetrics::WebauthnOptInParameters::kWithCreationChallenge, 1); + histogram_tester.ExpectTotalCount(promo_shown_histogram_name, 2); + histogram_tester.ExpectUniqueSample( + promo_user_decision_histogram_name, + AutofillMetrics::WebauthnOptInPromoUserDecisionMetric::kAccepted, 2); +} + +// Ensure proper metrics are logged when user opts-out from settings page. +TEST_F(CreditCardAccessManagerTest, SettingsPage_OptOut) { + base::HistogramTester histogram_tester; + std::string histogram_name = + "Autofill.BetterAuth.OptOutCalled.FromSettingsPage"; + GetFIDOAuthenticator()->SetUserVerifiable(true); + SetUserOptedIn(false); + + credit_card_access_manager_->OnSettingsPageFIDOAuthToggled(false); + OptChange(AutofillClient::SUCCESS, /*user_is_opted_in=*/false); + + histogram_tester.ExpectTotalCount(histogram_name, 1); +} +#endif // defined(OS_ANDROID) +#endif // !defined(OS_IOS) // Ensures that |is_authentication_in_progress_| is set correctly. TEST_F(CreditCardAccessManagerTest, AuthenticationInProgress) { diff --git a/chromium/components/autofill/core/browser/payments/credit_card_cvc_authenticator.cc b/chromium/components/autofill/core/browser/payments/credit_card_cvc_authenticator.cc index 7a021d8d032..d8f1cb1c685 100644 --- a/chromium/components/autofill/core/browser/payments/credit_card_cvc_authenticator.cc +++ b/chromium/components/autofill/core/browser/payments/credit_card_cvc_authenticator.cc @@ -41,14 +41,16 @@ void CreditCardCVCAuthenticator::OnFullCardRequestSucceeded( const payments::FullCardRequest& full_card_request, const CreditCard& card, const base::string16& cvc) { + payments::PaymentsClient::UnmaskResponseDetails response = + full_card_request.unmask_response_details(); requester_->OnCVCAuthenticationComplete( CVCAuthenticationResponse() .with_did_succeed(true) .with_card(&card) .with_cvc(cvc) - .with_creation_options( - std::move(full_card_request.unmask_response_details() - .fido_creation_options))); + .with_creation_options(std::move(response.fido_creation_options)) + .with_request_options(std::move(response.fido_request_options)) + .with_card_authorization_token(response.card_authorization_token)); } void CreditCardCVCAuthenticator::OnFullCardRequestFailed() { diff --git a/chromium/components/autofill/core/browser/payments/credit_card_cvc_authenticator.h b/chromium/components/autofill/core/browser/payments/credit_card_cvc_authenticator.h index 05ad5b7f02e..a470eb83e10 100644 --- a/chromium/components/autofill/core/browser/payments/credit_card_cvc_authenticator.h +++ b/chromium/components/autofill/core/browser/payments/credit_card_cvc_authenticator.h @@ -42,10 +42,21 @@ class CreditCardCVCAuthenticator creation_options = std::move(v); return *this; } + CVCAuthenticationResponse& with_request_options( + base::Optional<base::Value> v) { + request_options = std::move(v); + return *this; + } + CVCAuthenticationResponse& with_card_authorization_token(std::string s) { + card_authorization_token = s; + return *this; + } bool did_succeed = false; const CreditCard* card = nullptr; base::string16 cvc = base::string16(); base::Optional<base::Value> creation_options = base::nullopt; + base::Optional<base::Value> request_options = base::nullopt; + std::string card_authorization_token = std::string(); }; class Requester { public: diff --git a/chromium/components/autofill/core/browser/payments/credit_card_cvc_authenticator_unittest.cc b/chromium/components/autofill/core/browser/payments/credit_card_cvc_authenticator_unittest.cc index 58a13a370b8..d036800e783 100644 --- a/chromium/components/autofill/core/browser/payments/credit_card_cvc_authenticator_unittest.cc +++ b/chromium/components/autofill/core/browser/payments/credit_card_cvc_authenticator_unittest.cc @@ -51,6 +51,7 @@ #include "components/autofill/core/common/autofill_payments_features.h" #include "components/autofill/core/common/autofill_prefs.h" #include "components/autofill/core/common/autofill_switches.h" +#include "components/autofill/core/common/autofill_tick_clock.h" #include "components/autofill/core/common/autofill_util.h" #include "components/prefs/pref_service.h" #include "components/security_state/core/security_state.h" @@ -75,13 +76,13 @@ const char kTestNumber[] = "4234567890123456"; // Visa std::string NextYear() { base::Time::Exploded now; - base::Time::Now().LocalExplode(&now); + AutofillClock::Now().LocalExplode(&now); return base::NumberToString(now.year + 1); } std::string NextMonth() { base::Time::Exploded now; - base::Time::Now().LocalExplode(&now); + AutofillClock::Now().LocalExplode(&now); return base::NumberToString(now.month % 12 + 1); } @@ -144,6 +145,13 @@ class CreditCardCVCAuthenticatorTest : public testing::Test { payments::FullCardRequest* full_card_request = cvc_authenticator_->full_card_request_.get(); DCHECK(full_card_request); + + // Mock user response. + payments::FullCardRequest::UserProvidedUnmaskDetails details; + details.cvc = base::ASCIIToUTF16("123"); + full_card_request->OnUnmaskPromptAccepted(details); + + // Mock payments response. payments::PaymentsClient::UnmaskResponseDetails response; full_card_request->OnDidGetRealPan(result, response.with_real_pan(real_pan)); @@ -165,7 +173,7 @@ TEST_F(CreditCardCVCAuthenticatorTest, AuthenticateServerCardSuccess) { cvc_authenticator_->Authenticate(&card, requester_->GetWeakPtr(), &personal_data_manager_, - base::TimeTicks::Now()); + AutofillTickClock::NowTicks()); OnDidGetRealPan(AutofillClient::SUCCESS, kTestNumber); EXPECT_TRUE(requester_->did_succeed()); @@ -177,7 +185,7 @@ TEST_F(CreditCardCVCAuthenticatorTest, AuthenticateServerCardNetworkError) { cvc_authenticator_->Authenticate(&card, requester_->GetWeakPtr(), &personal_data_manager_, - base::TimeTicks::Now()); + AutofillTickClock::NowTicks()); OnDidGetRealPan(AutofillClient::NETWORK_ERROR, std::string()); EXPECT_FALSE(requester_->did_succeed()); @@ -188,7 +196,7 @@ TEST_F(CreditCardCVCAuthenticatorTest, AuthenticateServerCardPermanentFailure) { cvc_authenticator_->Authenticate(&card, requester_->GetWeakPtr(), &personal_data_manager_, - base::TimeTicks::Now()); + AutofillTickClock::NowTicks()); OnDidGetRealPan(AutofillClient::PERMANENT_FAILURE, std::string()); EXPECT_FALSE(requester_->did_succeed()); @@ -199,7 +207,7 @@ TEST_F(CreditCardCVCAuthenticatorTest, AuthenticateServerCardTryAgainFailure) { cvc_authenticator_->Authenticate(&card, requester_->GetWeakPtr(), &personal_data_manager_, - base::TimeTicks::Now()); + AutofillTickClock::NowTicks()); OnDidGetRealPan(AutofillClient::TRY_AGAIN_FAILURE, std::string()); EXPECT_FALSE(requester_->did_succeed()); diff --git a/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc b/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc index e6c15670b42..6cb340e60d9 100644 --- a/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc +++ b/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc @@ -12,8 +12,11 @@ #include "base/containers/flat_set.h" #include "base/strings/string16.h" #include "base/strings/string_util.h" +#include "build/build_config.h" #include "components/autofill/core/browser/autofill_client.h" +#include "components/autofill/core/browser/autofill_metrics.h" #include "components/autofill/core/browser/data_model/credit_card.h" +#include "components/autofill/core/browser/payments/fido_authentication_strike_database.h" #include "components/autofill/core/browser/payments/payments_client.h" #include "components/autofill/core/browser/personal_data_manager.h" #include "components/autofill/core/common/autofill_payments_features.h" @@ -28,8 +31,6 @@ namespace autofill { namespace { // Default timeout for user to respond to WebAuthn prompt. constexpr int kWebAuthnTimeoutMs = 3 * 60 * 1000; // 3 minutes -// Timeout to wait for synchronous version of IsUserVerifiable(). -constexpr int kIsUserVerifiableTimeoutMs = 1000; constexpr char kGooglePaymentsRpid[] = "google.com"; constexpr char kGooglePaymentsRpName[] = "Google Payments"; @@ -60,10 +61,14 @@ CreditCardFIDOAuthenticator::CreditCardFIDOAuthenticator(AutofillDriver* driver, CreditCardFIDOAuthenticator::~CreditCardFIDOAuthenticator() {} -void CreditCardFIDOAuthenticator::ShowWebauthnOfferDialog() { +void CreditCardFIDOAuthenticator::ShowWebauthnOfferDialog( + std::string card_authorization_token) { + card_authorization_token_ = card_authorization_token; autofill_client_->ShowWebauthnOfferDialog(base::BindRepeating( &CreditCardFIDOAuthenticator::OnWebauthnOfferDialogUserResponse, weak_ptr_factory_.GetWeakPtr())); + AutofillMetrics::LogWebauthnOptInPromoShown( + /*is_checkout_flow=*/!card_authorization_token_.empty()); } void CreditCardFIDOAuthenticator::Authenticate( @@ -83,31 +88,50 @@ void CreditCardFIDOAuthenticator::Authenticate( } } -void CreditCardFIDOAuthenticator::Register(base::Value creation_options) { +void CreditCardFIDOAuthenticator::Register(std::string card_authorization_token, + base::Value creation_options) { // If |creation_options| is set, then must enroll a new credential. Otherwise // directly send request to payments for opting in. + card_authorization_token_ = card_authorization_token; if (creation_options.is_dict()) { if (IsValidCreationOptions(creation_options)) { current_flow_ = OPT_IN_WITH_CHALLENGE_FLOW; MakeCredential(ParseCreationOptions(creation_options)); } } else { - current_flow_ = OPT_IN_WITHOUT_CHALLENGE_FLOW; - OptChange(/*opt_in=*/true); + current_flow_ = OPT_IN_FETCH_CHALLENGE_FLOW; + OptChange(); + } +} + +void CreditCardFIDOAuthenticator::Authorize( + std::string card_authorization_token, + base::Value request_options) { + card_authorization_token_ = card_authorization_token; + if (IsValidRequestOptions(request_options)) { + // If user is already opted-in, then a new card is trying to be + // authorized. Otherwise, a user with a credential on file is trying to + // opt-in. + current_flow_ = IsUserOptedIn() ? FOLLOWUP_AFTER_CVC_AUTH_FLOW + : OPT_IN_WITH_CHALLENGE_FLOW; + GetAssertion(ParseRequestOptions(std::move(request_options))); } } void CreditCardFIDOAuthenticator::OptOut() { current_flow_ = OPT_OUT_FLOW; - OptChange(/*opt_in=*/false); + card_authorization_token_ = std::string(); + OptChange(); } void CreditCardFIDOAuthenticator::IsUserVerifiable( base::OnceCallback<void(bool)> callback) { if (base::FeatureList::IsEnabled( features::kAutofillCreditCardAuthentication)) { - autofill_driver_->ConnectToAuthenticator( - authenticator_.BindNewPipeAndPassReceiver()); + if (!authenticator_.is_bound()) { + autofill_driver_->ConnectToAuthenticator( + authenticator_.BindNewPipeAndPassReceiver()); + } authenticator_->IsUserVerifyingPlatformAuthenticatorAvailable( std::move(callback)); } else { @@ -115,21 +139,6 @@ void CreditCardFIDOAuthenticator::IsUserVerifiable( } } -bool CreditCardFIDOAuthenticator::IsUserVerifiable() { - if (user_is_verifiable_.has_value()) - return user_is_verifiable_.value(); - - IsUserVerifiable( - base::BindOnce(&CreditCardFIDOAuthenticator::SetUserIsVerifiable, - weak_ptr_factory_.GetWeakPtr())); - - user_is_verifiable_callback_received_.declare_only_used_while_idle(); - user_is_verifiable_callback_received_.TimedWait( - base::TimeDelta::FromMilliseconds(kIsUserVerifiableTimeoutMs)); - - return user_is_verifiable_.value_or(false); -} - bool CreditCardFIDOAuthenticator::IsUserOptedIn() { return base::FeatureList::IsEnabled( features::kAutofillCreditCardAuthentication) && @@ -157,10 +166,41 @@ void CreditCardFIDOAuthenticator::SyncUserOptIn( is_user_opted_in); } +FidoAuthenticationStrikeDatabase* +CreditCardFIDOAuthenticator::GetOrCreateFidoAuthenticationStrikeDatabase() { + if (!fido_authentication_strike_database_) { + fido_authentication_strike_database_ = + std::make_unique<FidoAuthenticationStrikeDatabase>( + FidoAuthenticationStrikeDatabase( + autofill_client_->GetStrikeDatabase())); + } + return fido_authentication_strike_database_.get(); +} + void CreditCardFIDOAuthenticator::GetAssertion( PublicKeyCredentialRequestOptionsPtr request_options) { - autofill_driver_->ConnectToAuthenticator( - authenticator_.BindNewPipeAndPassReceiver()); + if (!authenticator_.is_bound()) { + autofill_driver_->ConnectToAuthenticator( + authenticator_.BindNewPipeAndPassReceiver()); + } +#if !defined(OS_ANDROID) + // On desktop, during an opt-in flow, close the WebAuthn offer dialog and get + // ready to show the OS level authentication dialog. If dialog is already + // closed, then the offer was declined during the fetching challenge process, + // and thus returned early. + if (current_flow_ == OPT_IN_WITH_CHALLENGE_FLOW) { + if (autofill_client_->CloseWebauthnOfferDialog()) { + // Now that the dialog has closed and will proceed to a WebAuthn prompt, + // the user must have accepted the dialog without cancelling. + AutofillMetrics::LogWebauthnOptInPromoUserDecision( + /*is_checkout_flow=*/!card_authorization_token_.empty(), + AutofillMetrics::WebauthnOptInPromoUserDecisionMetric::kAccepted); + } else { + current_flow_ = NONE_FLOW; + return; + } + } +#endif authenticator_->GetAssertion( std::move(request_options), base::BindOnce(&CreditCardFIDOAuthenticator::OnDidGetAssertion, @@ -169,92 +209,222 @@ void CreditCardFIDOAuthenticator::GetAssertion( void CreditCardFIDOAuthenticator::MakeCredential( PublicKeyCredentialCreationOptionsPtr creation_options) { - autofill_driver_->ConnectToAuthenticator( - authenticator_.BindNewPipeAndPassReceiver()); + if (!authenticator_.is_bound()) { + autofill_driver_->ConnectToAuthenticator( + authenticator_.BindNewPipeAndPassReceiver()); + } +#if !defined(OS_ANDROID) + // On desktop, close the WebAuthn offer dialog and get ready to show the OS + // level authentication dialog. If dialog is already closed, then the offer + // was declined during the fetching challenge process, and thus returned + // early. + if (autofill_client_->CloseWebauthnOfferDialog()) { + // Now that the dialog has closed and will proceed to a WebAuthn prompt, + // the user must have accepted the dialog without cancelling. + AutofillMetrics::LogWebauthnOptInPromoUserDecision( + /*is_checkout_flow=*/!card_authorization_token_.empty(), + AutofillMetrics::WebauthnOptInPromoUserDecisionMetric::kAccepted); + } else { + current_flow_ = NONE_FLOW; + return; + } +#endif authenticator_->MakeCredential( std::move(creation_options), base::BindOnce(&CreditCardFIDOAuthenticator::OnDidMakeCredential, weak_ptr_factory_.GetWeakPtr())); } -void CreditCardFIDOAuthenticator::OptChange(bool opt_in, - base::Value attestation_response) { +void CreditCardFIDOAuthenticator::OptChange( + base::Value authenticator_response) { payments::PaymentsClient::OptChangeRequestDetails request_details; request_details.app_locale = autofill_client_->GetPersonalDataManager()->app_locale(); - request_details.opt_in = opt_in; - if (attestation_response.is_dict()) { + + switch (current_flow_) { + case OPT_IN_WITH_CHALLENGE_FLOW: + case OPT_IN_FETCH_CHALLENGE_FLOW: + request_details.reason = + payments::PaymentsClient::OptChangeRequestDetails::ENABLE_FIDO_AUTH; + break; + case OPT_OUT_FLOW: + request_details.reason = + payments::PaymentsClient::OptChangeRequestDetails::DISABLE_FIDO_AUTH; + break; + case FOLLOWUP_AFTER_CVC_AUTH_FLOW: + request_details.reason = payments::PaymentsClient:: + OptChangeRequestDetails::ADD_CARD_FOR_FIDO_AUTH; + break; + default: + NOTREACHED(); + break; + } + + // If |authenticator_response| is set, that means the user just signed a + // challenge. In which case, if |card_authorization_token_| is not empty, then + // that will be required to bind a previous CVC check with this signature. + // This will opt the user in and authorize the card corresponding to + // |card_authorization_token_|. + // If |authenticator_response| is not set, that means the user was fetching a + // challenge, in which case |card_authorization_token_| will be required for + // the subsequent OptChange call. + AutofillMetrics::WebauthnOptInParameters opt_change_metric; + bool is_checkout_flow = !card_authorization_token_.empty(); + if (authenticator_response.is_dict()) { request_details.fido_authenticator_response = - std::move(attestation_response); + std::move(authenticator_response); + opt_change_metric = + request_details.fido_authenticator_response.FindKey( + "fido_assertion_info") + ? AutofillMetrics::WebauthnOptInParameters::kWithRequestChallenge + : AutofillMetrics::WebauthnOptInParameters::kWithCreationChallenge; + if (!card_authorization_token_.empty()) { + request_details.card_authorization_token = card_authorization_token_; + card_authorization_token_ = std::string(); + } + } else { + opt_change_metric = + AutofillMetrics::WebauthnOptInParameters::kFetchingChallenge; } payments_client_->OptChange( request_details, base::BindOnce(&CreditCardFIDOAuthenticator::OnDidGetOptChangeResult, weak_ptr_factory_.GetWeakPtr())); + + // Logging call if user was attempting to change their opt-in state. + if (current_flow_ != FOLLOWUP_AFTER_CVC_AUTH_FLOW) { + bool request_to_opt_in = (current_flow_ != OPT_OUT_FLOW); + AutofillMetrics::LogWebauthnOptChangeCalled( + request_to_opt_in, is_checkout_flow, opt_change_metric); + } } void CreditCardFIDOAuthenticator::OnDidGetAssertion( AuthenticatorStatus status, GetAssertionAuthenticatorResponsePtr assertion_response) { + LogWebauthnResult(status); + // End the flow if there was an authentication error. if (status != AuthenticatorStatus::SUCCESS) { + // Report failure to |requester_| if card unmasking was requested. + if (current_flow_ == AUTHENTICATION_FLOW) + requester_->OnFIDOAuthenticationComplete(/*did_succeed=*/false); + + // Treat failure to perform user verification as a strong signal not to + // offer opt-in in the future. + if (current_flow_ == OPT_IN_WITH_CHALLENGE_FLOW) { + GetOrCreateFidoAuthenticationStrikeDatabase()->AddStrikes( + FidoAuthenticationStrikeDatabase:: + kStrikesToAddWhenUserVerificationFailsOnOptInAttempt); + } + current_flow_ = NONE_FLOW; - requester_->OnFIDOAuthenticationComplete(/*did_succeed=*/false); return; } - base::Value response = ParseAssertionResponse(std::move(assertion_response)); - full_card_request_.reset(new payments::FullCardRequest( - autofill_client_, autofill_client_->GetPaymentsClient(), - autofill_client_->GetPersonalDataManager(), form_parsed_timestamp_)); - full_card_request_->GetFullCardViaFIDO( - *card_, AutofillClient::UNMASK_FOR_AUTOFILL, - weak_ptr_factory_.GetWeakPtr(), std::move(response)); + if (current_flow_ == AUTHENTICATION_FLOW) { + base::Value response = + ParseAssertionResponse(std::move(assertion_response)); + full_card_request_.reset(new payments::FullCardRequest( + autofill_client_, autofill_client_->GetPaymentsClient(), + autofill_client_->GetPersonalDataManager(), form_parsed_timestamp_)); + full_card_request_->GetFullCardViaFIDO( + *card_, AutofillClient::UNMASK_FOR_AUTOFILL, + weak_ptr_factory_.GetWeakPtr(), std::move(response)); + } else { + DCHECK(current_flow_ == FOLLOWUP_AFTER_CVC_AUTH_FLOW || + current_flow_ == OPT_IN_WITH_CHALLENGE_FLOW); + base::Value response = base::Value(base::Value::Type::DICTIONARY); + response.SetKey("fido_assertion_info", + ParseAssertionResponse(std::move(assertion_response))); + OptChange(std::move(response)); + } } void CreditCardFIDOAuthenticator::OnDidMakeCredential( AuthenticatorStatus status, MakeCredentialAuthenticatorResponsePtr attestation_response) { + LogWebauthnResult(status); + // End the flow if there was an authentication error. if (status != AuthenticatorStatus::SUCCESS) { + // Treat failure to perform user verification as a strong signal not to + // offer opt-in in the future. + if (current_flow_ == OPT_IN_WITH_CHALLENGE_FLOW) { + GetOrCreateFidoAuthenticationStrikeDatabase()->AddStrikes( + FidoAuthenticationStrikeDatabase:: + kStrikesToAddWhenUserVerificationFailsOnOptInAttempt); + } + current_flow_ = NONE_FLOW; return; } - OptChange(/*opt_in=*/true, - ParseAttestationResponse(std::move(attestation_response))); + OptChange(ParseAttestationResponse(std::move(attestation_response))); } void CreditCardFIDOAuthenticator::OnDidGetOptChangeResult( AutofillClient::PaymentsRpcResult result, - bool user_is_opted_in, - base::Value creation_options) { - DCHECK(current_flow_ == OPT_IN_WITHOUT_CHALLENGE_FLOW || + payments::PaymentsClient::OptChangeResponseDetails& response) { + DCHECK(current_flow_ == OPT_IN_FETCH_CHALLENGE_FLOW || current_flow_ == OPT_OUT_FLOW || - current_flow_ == OPT_IN_WITH_CHALLENGE_FLOW); + current_flow_ == OPT_IN_WITH_CHALLENGE_FLOW || + current_flow_ == FOLLOWUP_AFTER_CVC_AUTH_FLOW); + + // Update user preference to keep in sync with server. + ::autofill::prefs::SetCreditCardFIDOAuthEnabled( + autofill_client_->GetPrefs(), + response.user_is_opted_in.value_or(IsUserOptedIn())); + // End the flow if the server responded with an error. if (result != AutofillClient::PaymentsRpcResult::SUCCESS) { + if (current_flow_ == OPT_IN_FETCH_CHALLENGE_FLOW) + autofill_client_->UpdateWebauthnOfferDialogWithError(); current_flow_ = NONE_FLOW; return; } - // If response contains |creation_options| and the last opt-in attempt did not - // include a challenge, then invoke WebAuthn registration prompt. Otherwise, - // set pref to enable FIDO Authentication for card unmask and end the flow. - if (creation_options.is_dict() && - current_flow_ == OPT_IN_WITHOUT_CHALLENGE_FLOW) { - Register(std::move(creation_options)); + // If response contains |creation_options| or |request_options| and the last + // opt-in attempt did not include a challenge, then invoke WebAuthn + // registration/verification prompt. Otherwise end the flow. + if (current_flow_ == OPT_IN_FETCH_CHALLENGE_FLOW) { + if (response.fido_creation_options.has_value()) { + Register(card_authorization_token_, + std::move(response.fido_creation_options.value())); + } else if (response.fido_request_options.has_value()) { + Authorize(card_authorization_token_, + std::move(response.fido_request_options.value())); + } } else { - ::autofill::prefs::SetCreditCardFIDOAuthEnabled( - autofill_client_->GetPrefs(), user_is_opted_in); current_flow_ = NONE_FLOW; } } void CreditCardFIDOAuthenticator::OnWebauthnOfferDialogUserResponse( bool did_accept) { - // TODO(crbug.com/): Register and start fetching authentication challenge if - // |did_accept|, otherwise cancel any ongoing request. + if (did_accept) { + // Wait until GetAssertion()/MakeCredential() to log user acceptance, since + // user still has the option to cancel the dialog while the challenge is + // being fetched. + Register(card_authorization_token_); + } else { + // If user declined, log user decision. User may have initially accepted the + // dialog, but then chose to cancel while the challenge was being fetched. + AutofillMetrics::LogWebauthnOptInPromoUserDecision( + /*is_checkout_flow=*/!card_authorization_token_.empty(), + current_flow_ == OPT_IN_FETCH_CHALLENGE_FLOW + ? AutofillMetrics::WebauthnOptInPromoUserDecisionMetric:: + kDeclinedAfterAccepting + : AutofillMetrics::WebauthnOptInPromoUserDecisionMetric:: + kDeclinedImmediately); + payments_client_->CancelRequest(); + card_authorization_token_ = std::string(); + current_flow_ = NONE_FLOW; + GetOrCreateFidoAuthenticationStrikeDatabase()->AddStrikes( + FidoAuthenticationStrikeDatabase::kStrikesToAddWhenOptInOfferDeclined); + ::autofill::prefs::SetCreditCardFIDOAuthEnabled( + autofill_client_->GetPrefs(), false); + } } void CreditCardFIDOAuthenticator::OnFullCardRequestSucceeded( @@ -356,6 +526,23 @@ CreditCardFIDOAuthenticator::ParseCreationOptions( options->adjusted_timeout = base::TimeDelta::FromMilliseconds( timeout ? timeout->GetInt() : kWebAuthnTimeoutMs); + const auto* attestation = + creation_options.FindStringKey("attestation_conveyance_preference"); + if (!attestation || base::EqualsCaseInsensitiveASCII(*attestation, "NONE")) { + options->attestation = AttestationConveyancePreference::kNone; + } else if (base::EqualsCaseInsensitiveASCII(*attestation, "INDIRECT")) { + options->attestation = AttestationConveyancePreference::kIndirect; + } else if (base::EqualsCaseInsensitiveASCII(*attestation, "DIRECT")) { + options->attestation = AttestationConveyancePreference::kDirect; + } else { + NOTREACHED(); + } + + // Only allow user-verifying platform authenticators. + options->authenticator_selection = AuthenticatorSelectionCriteria( + AuthenticatorAttachment::kPlatform, /*require_resident_key=*/false, + UserVerificationRequirement::kRequired); + // List of keys that Payments already knows about, and so should not make a // new credential. const auto* excluded_keys_list = @@ -398,7 +585,8 @@ CreditCardFIDOAuthenticator::ParseCredentialDescriptor( base::Value CreditCardFIDOAuthenticator::ParseAssertionResponse( GetAssertionAuthenticatorResponsePtr assertion_response) { base::Value response = base::Value(base::Value::Type::DICTIONARY); - response.SetKey("credential_id", base::Value(assertion_response->info->id)); + response.SetKey("credential_id", + BytesToBase64(assertion_response->info->raw_id)); response.SetKey("authenticator_data", BytesToBase64(assertion_response->authenticator_data)); response.SetKey("client_data", @@ -423,7 +611,7 @@ base::Value CreditCardFIDOAuthenticator::ParseAttestationResponse( base::Value authenticator_transport_list = base::Value(base::Value::Type::LIST); for (FidoTransportProtocol protocol : attestation_response->transports) { - authenticator_transport_list.GetList().push_back( + authenticator_transport_list.Append( base::Value(base::ToUpperASCII(device::ToString(protocol)))); } @@ -462,9 +650,38 @@ bool CreditCardFIDOAuthenticator::IsValidCreationOptions( creation_options.FindStringKey("challenge"); } -void CreditCardFIDOAuthenticator::SetUserIsVerifiable(bool user_is_verifiable) { - user_is_verifiable_ = user_is_verifiable; - user_is_verifiable_callback_received_.Signal(); -} +void CreditCardFIDOAuthenticator::LogWebauthnResult( + AuthenticatorStatus status) { + AutofillMetrics::WebauthnFlowEvent event; + switch (current_flow_) { + case AUTHENTICATION_FLOW: + event = AutofillMetrics::WebauthnFlowEvent::kImmediateAuthentication; + break; + case FOLLOWUP_AFTER_CVC_AUTH_FLOW: + event = AutofillMetrics::WebauthnFlowEvent::kAuthenticationAfterCvc; + break; + case OPT_IN_WITH_CHALLENGE_FLOW: + event = card_authorization_token_.empty() + ? AutofillMetrics::WebauthnFlowEvent::kSettingsPageOptIn + : AutofillMetrics::WebauthnFlowEvent::kCheckoutOptIn; + break; + default: + NOTREACHED(); + return; + } + AutofillMetrics::WebauthnResultMetric metric; + switch (status) { + case AuthenticatorStatus::SUCCESS: + metric = AutofillMetrics::WebauthnResultMetric::kSuccess; + break; + case AuthenticatorStatus::NOT_ALLOWED_ERROR: + metric = AutofillMetrics::WebauthnResultMetric::kNotAllowedError; + break; + default: + metric = AutofillMetrics::WebauthnResultMetric::kOtherError; + break; + } + AutofillMetrics::LogWebauthnResult(event, metric); +} } // namespace autofill diff --git a/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.h b/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.h index 01aef853526..e7d89ad1950 100644 --- a/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.h +++ b/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator.h @@ -11,6 +11,7 @@ #include "components/autofill/core/browser/autofill_client.h" #include "components/autofill/core/browser/autofill_driver.h" #include "components/autofill/core/browser/data_model/credit_card.h" +#include "components/autofill/core/browser/payments/fido_authentication_strike_database.h" #include "components/autofill/core/browser/payments/full_card_request.h" #include "components/autofill/core/browser/payments/payments_client.h" #include "device/fido/fido_constants.h" @@ -29,6 +30,9 @@ using blink::mojom::PublicKeyCredentialCreationOptions; using blink::mojom::PublicKeyCredentialCreationOptionsPtr; using blink::mojom::PublicKeyCredentialRequestOptions; using blink::mojom::PublicKeyCredentialRequestOptionsPtr; +using device::AttestationConveyancePreference; +using device::AuthenticatorAttachment; +using device::AuthenticatorSelectionCriteria; using device::CredentialType; using device::FidoTransportProtocol; using device::PublicKeyCredentialDescriptor; @@ -53,9 +57,11 @@ class CreditCardFIDOAuthenticator // Registration flow, including a challenge to sign. OPT_IN_WITH_CHALLENGE_FLOW, // Opt-in attempt flow, no challenge to sign. - OPT_IN_WITHOUT_CHALLENGE_FLOW, + OPT_IN_FETCH_CHALLENGE_FLOW, // Opt-out flow. OPT_OUT_FLOW, + // Authorization of a new card. + FOLLOWUP_AFTER_CVC_AUTH_FLOW, }; class Requester { public: @@ -68,16 +74,25 @@ class CreditCardFIDOAuthenticator ~CreditCardFIDOAuthenticator() override; // Offer the option to use WebAuthn for authenticating future card unmasking. - void ShowWebauthnOfferDialog(); + void ShowWebauthnOfferDialog(std::string card_authorization_token); - // Authentication + // Invokes Authentication flow. Responds to |accessor_| with full pan. void Authenticate(const CreditCard* card, base::WeakPtr<Requester> requester, base::TimeTicks form_parsed_timestamp, base::Value request_options); - // Registration - void Register(base::Value creation_options = base::Value()); + // Invokes Registration flow. Sends credentials created from + // |creation_options| along with the |card_authorization_token| to Payments in + // order to enroll the user and authorize the corresponding card. + void Register(std::string card_authorization_token = std::string(), + base::Value creation_options = base::Value()); + + // Invokes an Authorization flow. Sends signature created from + // |request_options| along with the |card_authorization_token| to Payments in + // order to authorize the corresponding card. + void Authorize(std::string card_authorization_token, + base::Value request_options); // Opts the user out. void OptOut(); @@ -87,15 +102,16 @@ class CreditCardFIDOAuthenticator // and enabled. Otherwise invokes callback with false. virtual void IsUserVerifiable(base::OnceCallback<void(bool)> callback); - // The synchronous version of IsUserVerifiable. Used on settings page load. - bool IsUserVerifiable(); - // Returns true only if the user has opted-in to use WebAuthn for autofill. virtual bool IsUserOptedIn(); // Ensures that local user opt-in pref is in-sync with payments server. void SyncUserOptIn(AutofillClient::UnmaskDetails& unmask_details); + // Retrieves the strike database for offering FIDO authentication. + FidoAuthenticationStrikeDatabase* + GetOrCreateFidoAuthenticationStrikeDatabase(); + // Returns the current flow. Flow current_flow() { return current_flow_; } @@ -124,7 +140,7 @@ class CreditCardFIDOAuthenticator PublicKeyCredentialCreationOptionsPtr creation_options); // Makes a request to payments to either opt-in or opt-out the user. - void OptChange(bool opt_in, base::Value attestation_response = base::Value()); + void OptChange(base::Value authenticator_response = base::Value()); // The callback invoked from the WebAuthn prompt including the // |assertion_response|, which will be sent to Google Payments to retrieve @@ -141,9 +157,9 @@ class CreditCardFIDOAuthenticator MakeCredentialAuthenticatorResponsePtr attestation_response); // Sets prefstore to enable credit card authentication if rpc was successful. - void OnDidGetOptChangeResult(AutofillClient::PaymentsRpcResult result, - bool user_is_opted_in, - base::Value creation_options = base::Value()); + void OnDidGetOptChangeResult( + AutofillClient::PaymentsRpcResult result, + payments::PaymentsClient::OptChangeResponseDetails& response); // The callback invoked from the WebAuthn offer dialog when it is accepted or // declined/cancelled. @@ -184,8 +200,8 @@ class CreditCardFIDOAuthenticator // Returns true if |request_options| contains a challenge. bool IsValidCreationOptions(const base::Value& creation_options); - // Sets the value for |user_is_verifiable_|. - void SetUserIsVerifiable(bool user_is_verifiable); + // Logs the result of a WebAuthn prompt. + void LogWebauthnResult(AuthenticatorStatus status); // Card being unmasked. const CreditCard* card_; @@ -193,6 +209,10 @@ class CreditCardFIDOAuthenticator // The current flow in progress. Flow current_flow_ = NONE_FLOW; + // Token used for authorizing new cards. Helps tie CVC auth and FIDO calls + // together in order to support FIDO-only unmasking on future attempts. + std::string card_authorization_token_; + // Meant for histograms recorded in FullCardRequest. base::TimeTicks form_parsed_timestamp_; @@ -215,8 +235,10 @@ class CreditCardFIDOAuthenticator // Weak pointer to object that is requesting authentication. base::WeakPtr<Requester> requester_; - // Set when callback for IsUserVerifiable() is invoked with passed value. - base::Optional<bool> user_is_verifiable_ = base::nullopt; + // Strike database to ensure we limit the number of times we offer fido + // authentication. + std::unique_ptr<FidoAuthenticationStrikeDatabase> + fido_authentication_strike_database_; // Signaled when callback for IsUserVerifiable() is invoked. base::WaitableEvent user_is_verifiable_callback_received_; diff --git a/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator_unittest.cc b/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator_unittest.cc index 3ec70dc08e8..1146593947d 100644 --- a/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator_unittest.cc +++ b/chromium/components/autofill/core/browser/payments/credit_card_fido_authenticator_unittest.cc @@ -53,6 +53,7 @@ #include "components/autofill/core/common/autofill_payments_features.h" #include "components/autofill/core/common/autofill_prefs.h" #include "components/autofill/core/common/autofill_switches.h" +#include "components/autofill/core/common/autofill_tick_clock.h" #include "components/autofill/core/common/autofill_util.h" #include "components/prefs/pref_service.h" #include "components/security_state/core/security_state.h" @@ -85,10 +86,11 @@ constexpr char kTestChallenge[] = "VGhpcyBpcyBhIHRlc3QgY2hhbGxlbmdl"; const char kTestCredentialId[] = "VGhpcyBpcyBhIHRlc3QgQ3JlZGVudGlhbCBJRC4="; // Base64 encoding of "This is a test signature". const char kTestSignature[] = "VGhpcyBpcyBhIHRlc3Qgc2lnbmF0dXJl"; +const char kTestAuthToken[] = "dummy_card_authorization_token"; std::string NextMonth() { base::Time::Exploded now; - base::Time::Now().LocalExplode(&now); + AutofillClock::Now().LocalExplode(&now); return base::NumberToString(now.month % 12 + 1); } @@ -133,6 +135,8 @@ class CreditCardFIDOAuthenticatorTest : public testing::Test { autofill_client_.GetIdentityManager(), &personal_data_manager_); autofill_client_.set_test_payments_client( std::unique_ptr<payments::TestPaymentsClient>(payments_client)); + autofill_client_.set_test_strike_database( + std::make_unique<TestStrikeDatabase>()); fido_authenticator_ = std::make_unique<CreditCardFIDOAuthenticator>( autofill_driver_.get(), &autofill_client_); } @@ -218,9 +222,19 @@ class CreditCardFIDOAuthenticatorTest : public testing::Test { // Mocks an OptChange response from Payments Client. void OptChange(AutofillClient::PaymentsRpcResult result, bool user_is_opted_in, - base::Value creation_options = base::Value()) { - fido_authenticator_->OnDidGetOptChangeResult(result, user_is_opted_in, - std::move(creation_options)); + bool include_creation_options = false, + bool include_request_options = false) { + payments::PaymentsClient::OptChangeResponseDetails response; + response.user_is_opted_in = user_is_opted_in; + if (include_creation_options) { + response.fido_creation_options = + GetTestCreationOptions(kTestChallenge, kTestRelyingPartyId); + } + if (include_request_options) { + response.fido_request_options = GetTestRequestOptions( + kTestChallenge, kTestRelyingPartyId, kTestCredentialId); + } + fido_authenticator_->OnDidGetOptChangeResult(result, response); } protected: @@ -291,10 +305,6 @@ TEST_F(CreditCardFIDOAuthenticatorTest, IsUserVerifiable_False) { EXPECT_FALSE(requester_->is_user_verifiable().value()); } -TEST_F(CreditCardFIDOAuthenticatorTest, Sync_IsUserVerifiable_False) { - EXPECT_FALSE(fido_authenticator_->IsUserVerifiable()); -} - TEST_F(CreditCardFIDOAuthenticatorTest, ParseRequestOptions) { base::Value request_options_json = GetTestRequestOptions( kTestChallenge, kTestRelyingPartyId, kTestCredentialId); @@ -311,7 +321,7 @@ TEST_F(CreditCardFIDOAuthenticatorTest, ParseAssertionResponse) { GetAssertionAuthenticatorResponsePtr assertion_response_ptr = GetAssertionAuthenticatorResponse::New(); assertion_response_ptr->info = blink::mojom::CommonCredentialInfo::New(); - assertion_response_ptr->info->id = kTestCredentialId; + assertion_response_ptr->info->raw_id = Base64ToBytes(kTestCredentialId); assertion_response_ptr->signature = Base64ToBytes(kTestSignature); base::Value assertion_response_json = @@ -332,6 +342,14 @@ TEST_F(CreditCardFIDOAuthenticatorTest, ParseCreationOptions) { std::move(creation_options_json)); EXPECT_EQ(kTestChallenge, BytesToBase64(creation_options_ptr->challenge)); EXPECT_EQ(kTestRelyingPartyId, creation_options_ptr->relying_party.id); + + // Ensure only platform authenticators are allowed. + EXPECT_EQ(AuthenticatorAttachment::kPlatform, + creation_options_ptr->authenticator_selection + ->authenticator_attachment()); + EXPECT_EQ(UserVerificationRequirement::kRequired, + creation_options_ptr->authenticator_selection + ->user_verification_requirement()); } TEST_F(CreditCardFIDOAuthenticatorTest, ParseAttestationResponse) { @@ -351,7 +369,7 @@ TEST_F(CreditCardFIDOAuthenticatorTest, AuthenticateCard_BadRequestOptions) { CreditCard card = CreateServerCard(kTestGUID, kTestNumber); fido_authenticator_->Authenticate(&card, requester_->GetWeakPtr(), - base::TimeTicks::Now(), + AutofillTickClock::NowTicks(), base::Value(base::Value::Type::DICTIONARY)); EXPECT_FALSE(requester_->did_succeed()); } @@ -360,9 +378,10 @@ TEST_F(CreditCardFIDOAuthenticatorTest, AuthenticateCard_UserVerificationFailed) { CreditCard card = CreateServerCard(kTestGUID, kTestNumber); - fido_authenticator_->Authenticate(&card, requester_->GetWeakPtr(), - base::TimeTicks::Now(), - base::Value(base::Value::Type::DICTIONARY)); + fido_authenticator_->Authenticate( + &card, requester_->GetWeakPtr(), AutofillTickClock::NowTicks(), + GetTestRequestOptions(kTestChallenge, kTestRelyingPartyId, + kTestCredentialId)); TestCreditCardFIDOAuthenticator::GetAssertion(fido_authenticator_.get(), /*did_succeed=*/false); @@ -374,7 +393,7 @@ TEST_F(CreditCardFIDOAuthenticatorTest, CreditCard card = CreateServerCard(kTestGUID, kTestNumber); fido_authenticator_->Authenticate( - &card, requester_->GetWeakPtr(), base::TimeTicks::Now(), + &card, requester_->GetWeakPtr(), AutofillTickClock::NowTicks(), GetTestRequestOptions(kTestChallenge, kTestRelyingPartyId, kTestCredentialId)); EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::AUTHENTICATION_FLOW, @@ -392,7 +411,7 @@ TEST_F(CreditCardFIDOAuthenticatorTest, AuthenticateCard_Success) { CreditCard card = CreateServerCard(kTestGUID, kTestNumber); fido_authenticator_->Authenticate( - &card, requester_->GetWeakPtr(), base::TimeTicks::Now(), + &card, requester_->GetWeakPtr(), AutofillTickClock::NowTicks(), GetTestRequestOptions(kTestChallenge, kTestRelyingPartyId, kTestCredentialId)); EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::AUTHENTICATION_FLOW, @@ -410,31 +429,45 @@ TEST_F(CreditCardFIDOAuthenticatorTest, AuthenticateCard_Success) { TEST_F(CreditCardFIDOAuthenticatorTest, OptIn_PaymentsResponseError) { scoped_feature_list_.InitAndEnableFeature( features::kAutofillCreditCardAuthentication); + base::HistogramTester histogram_tester; + std::string histogram_name = + "Autofill.BetterAuth.OptInCalled.FromCheckoutFlow"; + EXPECT_FALSE(fido_authenticator_->IsUserOptedIn()); - fido_authenticator_->Register(); - EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::OPT_IN_WITHOUT_CHALLENGE_FLOW, + fido_authenticator_->Register(kTestAuthToken); + EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::OPT_IN_FETCH_CHALLENGE_FLOW, fido_authenticator_->current_flow()); // Mock payments response. OptChange(AutofillClient::PaymentsRpcResult::NETWORK_ERROR, /*user_is_opted_in=*/false); EXPECT_FALSE(fido_authenticator_->IsUserOptedIn()); + histogram_tester.ExpectUniqueSample( + histogram_name, + AutofillMetrics::WebauthnOptInParameters::kFetchingChallenge, 1); } TEST_F(CreditCardFIDOAuthenticatorTest, OptIn_Success) { scoped_feature_list_.InitAndEnableFeature( features::kAutofillCreditCardAuthentication); + base::HistogramTester histogram_tester; + std::string histogram_name = + "Autofill.BetterAuth.OptInCalled.FromCheckoutFlow"; + EXPECT_FALSE(fido_authenticator_->IsUserOptedIn()); - fido_authenticator_->Register(); - EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::OPT_IN_WITHOUT_CHALLENGE_FLOW, + fido_authenticator_->Register(kTestAuthToken); + EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::OPT_IN_FETCH_CHALLENGE_FLOW, fido_authenticator_->current_flow()); // Mock payments response. OptChange(AutofillClient::PaymentsRpcResult::SUCCESS, /*user_is_opted_in=*/true); EXPECT_TRUE(fido_authenticator_->IsUserOptedIn()); + histogram_tester.ExpectUniqueSample( + histogram_name, + AutofillMetrics::WebauthnOptInParameters::kFetchingChallenge, 1); } TEST_F(CreditCardFIDOAuthenticatorTest, Register_BadCreationOptions) { @@ -443,6 +476,7 @@ TEST_F(CreditCardFIDOAuthenticatorTest, Register_BadCreationOptions) { EXPECT_FALSE(fido_authenticator_->IsUserOptedIn()); fido_authenticator_->Register( + kTestAuthToken, GetTestCreationOptions(/*challenge=*/"", kTestRelyingPartyId)); EXPECT_FALSE(fido_authenticator_->IsUserOptedIn()); @@ -454,6 +488,7 @@ TEST_F(CreditCardFIDOAuthenticatorTest, Register_UserResponseFailure) { EXPECT_FALSE(fido_authenticator_->IsUserOptedIn()); fido_authenticator_->Register( + kTestAuthToken, GetTestCreationOptions(kTestChallenge, kTestRelyingPartyId)); EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::OPT_IN_WITH_CHALLENGE_FLOW, fido_authenticator_->current_flow()); @@ -467,9 +502,14 @@ TEST_F(CreditCardFIDOAuthenticatorTest, Register_UserResponseFailure) { TEST_F(CreditCardFIDOAuthenticatorTest, Register_Success) { scoped_feature_list_.InitAndEnableFeature( features::kAutofillCreditCardAuthentication); + base::HistogramTester histogram_tester; + std::string histogram_name = + "Autofill.BetterAuth.OptInCalled.FromCheckoutFlow"; + EXPECT_FALSE(fido_authenticator_->IsUserOptedIn()); fido_authenticator_->Register( + kTestAuthToken, GetTestCreationOptions(kTestChallenge, kTestRelyingPartyId)); EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::OPT_IN_WITH_CHALLENGE_FLOW, fido_authenticator_->current_flow()); @@ -480,22 +520,29 @@ TEST_F(CreditCardFIDOAuthenticatorTest, Register_Success) { OptChange(AutofillClient::PaymentsRpcResult::SUCCESS, /*user_is_opted_in=*/true); EXPECT_TRUE(fido_authenticator_->IsUserOptedIn()); + + histogram_tester.ExpectUniqueSample( + histogram_name, + AutofillMetrics::WebauthnOptInParameters::kWithCreationChallenge, 1); } TEST_F(CreditCardFIDOAuthenticatorTest, Register_EnrollAttemptReturnsCreationOptions) { scoped_feature_list_.InitAndEnableFeature( features::kAutofillCreditCardAuthentication); + base::HistogramTester histogram_tester; + std::string histogram_name = + "Autofill.BetterAuth.OptInCalled.FromCheckoutFlow"; + EXPECT_FALSE(fido_authenticator_->IsUserOptedIn()); - fido_authenticator_->Register(); - EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::OPT_IN_WITHOUT_CHALLENGE_FLOW, + fido_authenticator_->Register(kTestAuthToken); + EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::OPT_IN_FETCH_CHALLENGE_FLOW, fido_authenticator_->current_flow()); // Mock payments response with challenge to invoke enrollment flow. OptChange(AutofillClient::PaymentsRpcResult::SUCCESS, - /*user_is_opted_in=*/false, - GetTestCreationOptions(kTestChallenge, kTestRelyingPartyId)); + /*user_is_opted_in=*/false, /*include_creation_options=*/true); EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::OPT_IN_WITH_CHALLENGE_FLOW, fido_authenticator_->current_flow()); EXPECT_FALSE(fido_authenticator_->IsUserOptedIn()); @@ -506,11 +553,67 @@ TEST_F(CreditCardFIDOAuthenticatorTest, OptChange(AutofillClient::PaymentsRpcResult::SUCCESS, /*user_is_opted_in=*/true); EXPECT_TRUE(fido_authenticator_->IsUserOptedIn()); + + histogram_tester.ExpectTotalCount(histogram_name, 2); + histogram_tester.ExpectBucketCount( + histogram_name, + AutofillMetrics::WebauthnOptInParameters::kFetchingChallenge, 1); + histogram_tester.ExpectBucketCount( + histogram_name, + AutofillMetrics::WebauthnOptInParameters::kWithCreationChallenge, 1); +} + +TEST_F(CreditCardFIDOAuthenticatorTest, + Register_OptInAttemptReturnsRequestOptions) { + scoped_feature_list_.InitAndEnableFeature( + features::kAutofillCreditCardAuthentication); + EXPECT_FALSE(fido_authenticator_->IsUserOptedIn()); + + fido_authenticator_->Register(kTestAuthToken); + EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::OPT_IN_FETCH_CHALLENGE_FLOW, + fido_authenticator_->current_flow()); + + // Mock payments response with challenge to invoke opt-in flow. + OptChange(AutofillClient::PaymentsRpcResult::SUCCESS, + /*user_is_opted_in=*/false, /*include_creation_options=*/false, + /*include_request_options=*/true); + EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::OPT_IN_WITH_CHALLENGE_FLOW, + fido_authenticator_->current_flow()); + EXPECT_FALSE(fido_authenticator_->IsUserOptedIn()); + + // Mock user response and second payments response. + TestCreditCardFIDOAuthenticator::GetAssertion(fido_authenticator_.get(), + /*did_succeed=*/true); + OptChange(AutofillClient::PaymentsRpcResult::SUCCESS, + /*user_is_opted_in=*/true); + EXPECT_TRUE(fido_authenticator_->IsUserOptedIn()); +} + +TEST_F(CreditCardFIDOAuthenticatorTest, Register_NewCardAuthorization) { + scoped_feature_list_.InitAndEnableFeature( + features::kAutofillCreditCardAuthentication); + ::autofill::prefs::SetCreditCardFIDOAuthEnabled(autofill_client_.GetPrefs(), + true); + EXPECT_TRUE(fido_authenticator_->IsUserOptedIn()); + + fido_authenticator_->Authorize( + kTestAuthToken, GetTestRequestOptions(kTestChallenge, kTestRelyingPartyId, + kTestCredentialId)); + EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::FOLLOWUP_AFTER_CVC_AUTH_FLOW, + fido_authenticator_->current_flow()); + + // Mock user response and second payments response. + TestCreditCardFIDOAuthenticator::GetAssertion(fido_authenticator_.get(), + /*did_succeed=*/true); + OptChange(AutofillClient::PaymentsRpcResult::SUCCESS, + /*user_is_opted_in=*/true); + EXPECT_TRUE(fido_authenticator_->IsUserOptedIn()); } TEST_F(CreditCardFIDOAuthenticatorTest, OptOut_Success) { scoped_feature_list_.InitAndEnableFeature( features::kAutofillCreditCardAuthentication); + base::HistogramTester histogram_tester; ::autofill::prefs::SetCreditCardFIDOAuthEnabled(autofill_client_.GetPrefs(), true); @@ -524,6 +627,8 @@ TEST_F(CreditCardFIDOAuthenticatorTest, OptOut_Success) { OptChange(AutofillClient::PaymentsRpcResult::SUCCESS, /*user_is_opted_in=*/false); EXPECT_FALSE(fido_authenticator_->IsUserOptedIn()); + histogram_tester.ExpectTotalCount( + "Autofill.BetterAuth.OptOutCalled.FromSettingsPage", 1); } } // namespace autofill diff --git a/chromium/components/autofill/core/browser/payments/credit_card_save_manager.cc b/chromium/components/autofill/core/browser/payments/credit_card_save_manager.cc index 05b70b7d8dc..111a45e9567 100644 --- a/chromium/components/autofill/core/browser/payments/credit_card_save_manager.cc +++ b/chromium/components/autofill/core/browser/payments/credit_card_save_manager.cc @@ -41,7 +41,9 @@ #include "components/autofill/core/common/autofill_constants.h" #include "components/autofill/core/common/autofill_features.h" #include "components/autofill/core/common/autofill_payments_features.h" +#include "components/autofill/core/common/autofill_prefs.h" #include "components/autofill/core/common/autofill_util.h" +#include "components/prefs/pref_service.h" #include "components/signin/public/identity_manager/identity_manager.h" #include "url/gurl.h" @@ -311,6 +313,11 @@ void CreditCardSaveManager::OnDidUploadCard( // removed. GetCreditCardSaveStrikeDatabase()->ClearStrikes( base::UTF16ToUTF8(upload_request_.card.LastFourDigits())); + + // After a card is successfully saved to server, notifies the + // |personal_data_manager_|. PDM uses this information to update the avatar + // button UI. + personal_data_manager_->OnCreditCardSaved(/*is_local_card=*/false); } else if (show_save_prompt_.has_value() && show_save_prompt_.value()) { // If the upload failed and the bubble was actually shown (NOT just the // icon), count that as a strike against offering upload in the future. @@ -322,6 +329,9 @@ void CreditCardSaveManager::OnDidUploadCard( // Show credit card upload feedback. client_->CreditCardUploadCompleted(result == AutofillClient::SUCCESS); + + if (observer_for_testing_) + observer_for_testing_->OnShowCardSavedFeedback(); } CreditCardSaveStrikeDatabase* @@ -352,6 +362,23 @@ void CreditCardSaveManager::OnDidGetUploadDetails( if (observer_for_testing_) observer_for_testing_->OnReceivedGetUploadDetailsResponse(); if (result == AutofillClient::SUCCESS) { + LegalMessageLine::Parse(*legal_message, &legal_message_lines_, + /*escape_apostrophes=*/true); + + if (legal_message_lines_.empty()) { + // Parsing legal messages failed, so upload should not be offered. + // Offer local card save if card is not already saved locally. + if (!uploading_local_card_) { + AttemptToOfferCardLocalSave(from_dynamic_change_form_, + has_non_focusable_field_, + upload_request_.card); + } + upload_decision_metrics_ |= + AutofillMetrics::UPLOAD_NOT_OFFERED_INVALID_LEGAL_MESSAGE; + LogCardUploadDecisions(upload_decision_metrics_); + return; + } + // Do *not* call payments_client_->Prepare() here. We shouldn't send // credentials until the user has explicitly accepted a prompt to upload. if (!supported_card_bin_ranges.empty() && @@ -369,7 +396,6 @@ void CreditCardSaveManager::OnDidGetUploadDetails( return; } upload_request_.context_token = context_token; - legal_message_ = base::DictionaryValue::From(std::move(legal_message)); OfferCardUploadSave(); } else { // If the upload details request failed and we *know* we have all possible @@ -449,7 +475,7 @@ void CreditCardSaveManager::OfferCardUploadSave() { if (!is_mobile_build || show_save_prompt_.value_or(true)) { user_did_accept_upload_prompt_ = false; client_->ConfirmSaveCreditCardToCloud( - upload_request_.card, std::move(legal_message_), + upload_request_.card, legal_message_lines_, AutofillClient::SaveCreditCardOptions() .with_from_dynamic_change_form(from_dynamic_change_form_) .with_has_non_focusable_field(has_non_focusable_field_) @@ -750,25 +776,27 @@ void CreditCardSaveManager::OnUserDidDecideOnUploadSave( const AutofillClient::UserProvidedCardDetails& user_provided_card_details) { switch (user_decision) { case AutofillClient::ACCEPTED: -// On Android, requesting cardholder name or expiration date is a two step -// flow. -#if defined(OS_ANDROID) +#if defined(OS_ANDROID) || defined(OS_IOS) + // On Android and iOS, requesting cardholder name is a two step flow. if (should_request_name_from_user_) { client_->ConfirmAccountNameFixFlow(base::BindOnce( &CreditCardSaveManager::OnUserDidAcceptAccountNameFixFlow, weak_ptr_factory_.GetWeakPtr())); +#if defined(OS_ANDROID) + // On Android, requesting expiration date is a two step flow. } else if (should_request_expiration_date_from_user_) { client_->ConfirmExpirationDateFixFlow( upload_request_.card, base::BindOnce( &CreditCardSaveManager::OnUserDidAcceptExpirationDateFixFlow, weak_ptr_factory_.GetWeakPtr())); +#endif // defined(OS_ANDROID) } else { OnUserDidAcceptUploadHelper(user_provided_card_details); } #else OnUserDidAcceptUploadHelper(user_provided_card_details); -#endif +#endif // defined(OS_ANDROID) || defined(OS_IOS) break; case AutofillClient::DECLINED: @@ -780,7 +808,7 @@ void CreditCardSaveManager::OnUserDidDecideOnUploadSave( personal_data_manager_->OnUserAcceptedUpstreamOffer(); } -#if defined(OS_ANDROID) +#if defined(OS_ANDROID) || defined(OS_IOS) void CreditCardSaveManager::OnUserDidAcceptAccountNameFixFlow( const base::string16& cardholder_name) { DCHECK(should_request_name_from_user_); @@ -789,7 +817,9 @@ void CreditCardSaveManager::OnUserDidAcceptAccountNameFixFlow( /*expiration_date_month=*/base::string16(), /*expiration_date_year=*/base::string16()}); } +#endif +#if defined(OS_ANDROID) void CreditCardSaveManager::OnUserDidAcceptExpirationDateFixFlow( const base::string16& month, const base::string16& year) { diff --git a/chromium/components/autofill/core/browser/payments/credit_card_save_manager.h b/chromium/components/autofill/core/browser/payments/credit_card_save_manager.h index 0fe7b67101f..941d7c0f7ba 100644 --- a/chromium/components/autofill/core/browser/payments/credit_card_save_manager.h +++ b/chromium/components/autofill/core/browser/payments/credit_card_save_manager.h @@ -19,11 +19,14 @@ #include "components/autofill/core/browser/data_model/credit_card.h" #include "components/autofill/core/browser/form_structure.h" #include "components/autofill/core/browser/payments/credit_card_save_strike_database.h" +#include "components/autofill/core/browser/payments/legal_message_line.h" #include "components/autofill/core/browser/payments/local_card_migration_strike_database.h" #include "components/autofill/core/browser/payments/payments_client.h" #include "components/autofill/core/browser/personal_data_manager.h" #include "url/origin.h" +class SaveCardOfferObserver; + namespace autofill { // Manages logic for determining whether upload credit card save to Google @@ -79,12 +82,14 @@ class CreditCardSaveManager { // particular actions occur. class ObserverForTest { public: - virtual void OnOfferLocalSave() = 0; - virtual void OnDecideToRequestUploadSave() = 0; - virtual void OnReceivedGetUploadDetailsResponse() = 0; - virtual void OnSentUploadCardRequest() = 0; - virtual void OnReceivedUploadCardResponse() = 0; - virtual void OnStrikeChangeComplete() = 0; + virtual ~ObserverForTest() {} + virtual void OnOfferLocalSave() {} + virtual void OnDecideToRequestUploadSave() {} + virtual void OnReceivedGetUploadDetailsResponse() {} + virtual void OnSentUploadCardRequest() {} + virtual void OnReceivedUploadCardResponse() {} + virtual void OnShowCardSavedFeedback() {} + virtual void OnStrikeChangeComplete() {} }; // The parameters should outlive the CreditCardSaveManager. @@ -138,10 +143,14 @@ class CreditCardSaveManager { friend class TestCreditCardSaveManager; friend class SaveCardBubbleViewsFullFormBrowserTest; friend class SaveCardInfobarEGTestHelper; + friend class ::SaveCardOfferObserver; + FRIEND_TEST_ALL_PREFIXES( + SaveCardBubbleViewsFullFormBrowserTestWithAutofillUpstream, + StrikeDatabase_Upload_FullFlowTest); FRIEND_TEST_ALL_PREFIXES(SaveCardBubbleViewsFullFormBrowserTest, StrikeDatabase_Local_FullFlowTest); - FRIEND_TEST_ALL_PREFIXES(SaveCardBubbleViewsFullFormBrowserTest, - StrikeDatabase_Upload_FullFlowTest); + FRIEND_TEST_ALL_PREFIXES(SaveCardBubbleViewsFullFormBrowserTestForStatusChip, + Feedback_CardSavingAnimation); // Returns the CreditCardSaveStrikeDatabase for |client_|. CreditCardSaveStrikeDatabase* GetCreditCardSaveStrikeDatabase(); @@ -210,12 +219,14 @@ class CreditCardSaveManager { const AutofillClient::UserProvidedCardDetails& user_provided_card_details); -#if defined(OS_ANDROID) +#if defined(OS_ANDROID) || defined(OS_IOS) // Upload the card details with the user provided cardholder_name. // Only relevant for mobile as fix flow is two steps on mobile compared to // one step on desktop. void OnUserDidAcceptAccountNameFixFlow(const base::string16& cardholder_name); +#endif // defined(OS_ANDROID) || defined(OS_IOS) +#if defined(OS_ANDROID) // Upload the card details with the user provided expiration date month and // year. Only relevant for mobile as fix flow is two steps on mobile compared // to one step on desktop. @@ -332,8 +343,8 @@ class CreditCardSaveManager { // The origin of the top level frame from which a form is uploaded. url::Origin pending_upload_request_origin_; - // The returned legal message from a GetUploadDetails call to Google Payments. - std::unique_ptr<base::DictionaryValue> legal_message_; + // The parsed lines from the legal message returned from GetUploadDetails. + LegalMessageLines legal_message_lines_; std::unique_ptr<CreditCardSaveStrikeDatabase> credit_card_save_strike_database_; diff --git a/chromium/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc b/chromium/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc index 721fab518e0..bf87aceff4b 100644 --- a/chromium/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc @@ -17,6 +17,7 @@ #include "base/guid.h" #include "base/metrics/metrics_hashes.h" #include "base/strings/string16.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" @@ -75,18 +76,6 @@ using UkmDeveloperEngagementType = ukm::builders::Autofill_DeveloperEngagement; const base::Time kArbitraryTime = base::Time::FromDoubleT(25); const base::Time kMuchLaterTime = base::Time::FromDoubleT(5000); -std::string NextYear() { - base::Time::Exploded now; - base::Time::Now().LocalExplode(&now); - return std::to_string(now.year + 1); -} - -std::string NextMonth() { - base::Time::Exploded now; - base::Time::Now().LocalExplode(&now); - return std::to_string(now.month % 12 + 1); -} - // Used to configure form for |CreateTestCreditCardFormData|. struct CreditCardFormOptions { CreditCardFormOptions& with_is_https(bool b) { @@ -281,8 +270,8 @@ class CreditCardSaveManagerTest : public testing::Test { // Edit the data, and submit. form.fields[1].value = ASCIIToUTF16("4111111111111111"); - form.fields[2].value = ASCIIToUTF16(NextMonth()); - form.fields[3].value = ASCIIToUTF16(NextYear()); + form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + form.fields[3].value = ASCIIToUTF16(test::NextYear()); FormSubmitted(form); EXPECT_TRUE(autofill_client_.ConfirmSaveCardLocallyWasCalled()); } @@ -426,8 +415,8 @@ TEST_F(CreditCardSaveManagerTest, MAYBE_CreditCardSavedWhenAutocompleteOff) { // Edit the data, and submit. form.fields[1].value = ASCIIToUTF16("4111111111111111"); - form.fields[2].value = ASCIIToUTF16(NextMonth()); - form.fields[3].value = ASCIIToUTF16(NextYear()); + form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + form.fields[3].value = ASCIIToUTF16(test::NextYear()); FormSubmitted(form); EXPECT_TRUE(autofill_client_.ConfirmSaveCardLocallyWasCalled()); } @@ -445,8 +434,8 @@ TEST_F(CreditCardSaveManagerTest, InvalidCreditCardNumberIsNotSaved) { std::string card("4408041234567890"); ASSERT_FALSE(autofill::IsValidCreditCardNumber(ASCIIToUTF16(card))); form.fields[1].value = ASCIIToUTF16(card); - form.fields[2].value = ASCIIToUTF16(NextMonth()); - form.fields[3].value = ASCIIToUTF16(NextYear()); + form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + form.fields[3].value = ASCIIToUTF16(test::NextYear()); FormSubmitted(form); EXPECT_FALSE(autofill_client_.ConfirmSaveCardLocallyWasCalled()); } @@ -470,8 +459,8 @@ TEST_F(CreditCardSaveManagerTest, CreditCardDisabledDoesNotSave) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -505,8 +494,8 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_OnlyCountryInAddresses) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -570,8 +559,8 @@ TEST_F(CreditCardSaveManagerTest, LocalCreditCard_FirstAndLastName) { credit_card_form.fields[0].value = ASCIIToUTF16("Flo"); credit_card_form.fields[1].value = ASCIIToUTF16("Master"); credit_card_form.fields[2].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[3].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[4].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[4].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[5].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -636,8 +625,8 @@ TEST_F(CreditCardSaveManagerTest, LocalCreditCard_LastAndFirstName) { credit_card_form.fields[0].value = ASCIIToUTF16("Master"); credit_card_form.fields[1].value = ASCIIToUTF16("Flo"); credit_card_form.fields[2].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[3].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[4].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[4].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[5].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -709,8 +698,8 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_WithNonFocusableField) { credit_card_form.fields[0].value = ASCIIToUTF16("Flo"); credit_card_form.fields[1].value = ASCIIToUTF16("Master"); credit_card_form.fields[2].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[3].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[4].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[4].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[5].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -755,8 +744,8 @@ TEST_F(CreditCardSaveManagerTest, LocalCreditCard_WithNonFocusableField) { credit_card_form.fields[0].value = ASCIIToUTF16("Flo"); credit_card_form.fields[1].value = ASCIIToUTF16("Master"); credit_card_form.fields[2].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[3].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[4].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[4].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[5].value = ASCIIToUTF16("123"); FormSubmitted(credit_card_form); @@ -782,8 +771,8 @@ TEST_F(CreditCardSaveManagerTest, credit_card_form.fields[0].value = ASCIIToUTF16("Flo"); credit_card_form.fields[1].value = ASCIIToUTF16("Master"); credit_card_form.fields[2].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[3].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[4].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[4].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[5].value = ASCIIToUTF16("123"); FormSubmitted(credit_card_form); @@ -815,8 +804,8 @@ TEST_F(CreditCardSaveManagerTest, credit_card_form.fields[0].value = ASCIIToUTF16("Flo"); credit_card_form.fields[1].value = ASCIIToUTF16("Master"); credit_card_form.fields[2].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[3].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[4].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[4].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[5].value = ASCIIToUTF16("123"); FormSubmitted(credit_card_form); @@ -858,8 +847,8 @@ TEST_F( credit_card_form.fields[0].value = ASCIIToUTF16("Flo"); credit_card_form.fields[1].value = ASCIIToUTF16("Master"); credit_card_form.fields[2].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[3].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[4].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[4].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[5].value = ASCIIToUTF16("123"); FormSubmitted(credit_card_form); @@ -897,8 +886,8 @@ TEST_F(CreditCardSaveManagerTest, credit_card_form.fields[0].value = ASCIIToUTF16("Flo"); credit_card_form.fields[1].value = ASCIIToUTF16("Master"); credit_card_form.fields[2].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[3].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[4].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[4].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[5].value = ASCIIToUTF16("123"); FormSubmitted(credit_card_form); @@ -923,8 +912,8 @@ TEST_F(CreditCardSaveManagerTest, credit_card_form.fields[0].value = ASCIIToUTF16("Flo"); credit_card_form.fields[1].value = ASCIIToUTF16("Master"); credit_card_form.fields[2].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[3].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[4].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[4].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[5].value = ASCIIToUTF16("123"); FormSubmitted(credit_card_form); @@ -951,8 +940,8 @@ TEST_F(CreditCardSaveManagerTest, credit_card_form.fields[0].value = ASCIIToUTF16("Flo"); credit_card_form.fields[1].value = ASCIIToUTF16("Master"); credit_card_form.fields[2].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[3].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[4].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[4].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[5].value = ASCIIToUTF16("123"); FormSubmitted(credit_card_form); @@ -983,8 +972,8 @@ TEST_F(CreditCardSaveManagerTest, credit_card_form.fields[0].value = ASCIIToUTF16("Flo"); credit_card_form.fields[1].value = ASCIIToUTF16("Master"); credit_card_form.fields[2].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[3].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[4].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[4].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[5].value = ASCIIToUTF16("123"); FormSubmitted(credit_card_form); @@ -1025,8 +1014,8 @@ TEST_F( credit_card_form.fields[0].value = ASCIIToUTF16("Flo"); credit_card_form.fields[1].value = ASCIIToUTF16("Master"); credit_card_form.fields[2].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[3].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[4].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[4].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[5].value = ASCIIToUTF16("123"); FormSubmitted(credit_card_form); @@ -1063,8 +1052,8 @@ TEST_F(CreditCardSaveManagerTest, credit_card_form.fields[0].value = ASCIIToUTF16("Flo"); credit_card_form.fields[1].value = ASCIIToUTF16("Master"); credit_card_form.fields[2].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[3].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[4].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[4].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[5].value = ASCIIToUTF16("123"); FormSubmitted(credit_card_form); @@ -1090,8 +1079,8 @@ TEST_F(CreditCardSaveManagerTest, SaveCreditCardOptions_WithoutDynamicForms) { credit_card_form.fields[0].value = ASCIIToUTF16("Flo"); credit_card_form.fields[1].value = ASCIIToUTF16("Master"); credit_card_form.fields[2].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[3].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[4].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[4].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[5].value = ASCIIToUTF16("123"); FormSubmitted(credit_card_form); @@ -1125,8 +1114,8 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_FirstAndLastName) { credit_card_form.fields[0].value = ASCIIToUTF16("Flo"); credit_card_form.fields[1].value = ASCIIToUTF16("Master"); credit_card_form.fields[2].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[3].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[4].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[4].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[5].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -1199,8 +1188,8 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_LastAndFirstName) { credit_card_form.fields[0].value = ASCIIToUTF16("Master"); credit_card_form.fields[1].value = ASCIIToUTF16("Flo"); credit_card_form.fields[2].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[3].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[4].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[4].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[5].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -1251,8 +1240,8 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCardAndSaveCopy) { const char* const card_number = "4111111111111111"; credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16(card_number); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); FormSubmitted(credit_card_form); @@ -1268,8 +1257,12 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCardAndSaveCopy) { EXPECT_EQ(CreditCard::OK, saved_card->GetServerStatus()); EXPECT_EQ(base::ASCIIToUTF16("1111"), saved_card->LastFourDigits()); EXPECT_EQ(kVisaCard, saved_card->network()); - EXPECT_EQ(std::stoi(NextMonth()), saved_card->expiration_month()); - EXPECT_EQ(std::stoi(NextYear()), saved_card->expiration_year()); + int month; + EXPECT_TRUE(base::StringToInt(test::NextMonth(), &month)); + EXPECT_EQ(month, saved_card->expiration_month()); + int year; + EXPECT_TRUE(base::StringToInt(test::NextYear(), &year)); + EXPECT_EQ(year, saved_card->expiration_year()); EXPECT_EQ(server_id, saved_card->server_id()); EXPECT_EQ(CreditCard::FULL_SERVER_CARD, saved_card->record_type()); EXPECT_EQ(base::ASCIIToUTF16(card_number), saved_card->number()); @@ -1305,8 +1298,8 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_DisableLocalSave) { const char* const card_number = "4111111111111111"; credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16(card_number); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); FormSubmitted(credit_card_form); @@ -1335,8 +1328,8 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_FeatureNotEnabled) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -1370,8 +1363,8 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_CvcUnavailable) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16(""); // CVC MISSING base::HistogramTester histogram_tester; @@ -1408,8 +1401,8 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_CvcInvalidLength) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("1234"); base::HistogramTester histogram_tester; @@ -1465,8 +1458,8 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_MultipleCvcFields) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16(""); // CVC MISSING credit_card_form.fields[5].value = ASCIIToUTF16("123"); @@ -1516,8 +1509,8 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NoCvcFieldOnForm) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); base::HistogramTester histogram_tester; @@ -1571,8 +1564,8 @@ TEST_F(CreditCardSaveManagerTest, // Enter an invalid cvc in "Random Field" and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("1234"); base::HistogramTester histogram_tester; @@ -1627,8 +1620,8 @@ TEST_F(CreditCardSaveManagerTest, // Enter a valid cvc in "Random Field" and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -1685,8 +1678,8 @@ TEST_F(CreditCardSaveManagerTest, // Enter a valid cvc in "Random Field" and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -1717,8 +1710,8 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NoProfileAvailable) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Bob Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -1763,8 +1756,8 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NoRecentlyUsedProfile) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -1798,8 +1791,8 @@ TEST_F(CreditCardSaveManagerTest, // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16(""); // CVC MISSING base::HistogramTester histogram_tester; @@ -1839,8 +1832,8 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NoNameAvailable) { // Edit the data, but don't include a name, and submit. credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -1878,8 +1871,8 @@ TEST_F(CreditCardSaveManagerTest, // Edit the data, but don't include a name, and submit. credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -1936,8 +1929,8 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_ZipCodesConflict) { // Edit the data and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -1984,8 +1977,8 @@ TEST_F(CreditCardSaveManagerTest, // Edit the data and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -2031,8 +2024,8 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_ZipCodesHavePrefixMatch) { // Edit the data and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -2073,8 +2066,8 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NoZipCodeAvailable) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -2118,8 +2111,8 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_CCFormHasMiddleInitial) { // submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo W. Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -2157,8 +2150,8 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NoMiddleInitialInCCForm) { // Edit the data, but do not use middle initial. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -2192,8 +2185,8 @@ TEST_F(CreditCardSaveManagerTest, // Edit the name by adding a middle name. credit_card_form.fields[0].value = ASCIIToUTF16("John Quincy Adams"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -2236,8 +2229,8 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_CCFormHasAddressMiddleName) { // Edit the name by removing middle name. credit_card_form.fields[0].value = ASCIIToUTF16("John Adams"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -2289,8 +2282,8 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NamesCanMismatch) { // Edit the data, but use yet another name, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Bob Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -2346,8 +2339,8 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_IgnoreOldProfiles) { // Edit the data, but use yet another name, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Master Blaster"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -2386,8 +2379,8 @@ TEST_F( // Edit the data, but don't include a name, and submit. credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -2429,8 +2422,8 @@ TEST_F( // Edit the data, but include a conflicting name, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Jane Doe"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -2468,8 +2461,8 @@ TEST_F(CreditCardSaveManagerTest, // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -2504,8 +2497,8 @@ TEST_F(CreditCardSaveManagerTest, // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -2538,8 +2531,8 @@ TEST_F( // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -2584,8 +2577,8 @@ TEST_F( // Edit the data, but don't include a name, and submit. credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -2632,8 +2625,8 @@ TEST_F( // Edit the data, but include a conflicting name, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Jane Doe"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -2675,8 +2668,8 @@ TEST_F( // Edit the data, but don't include a name, and submit. credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -2718,8 +2711,8 @@ TEST_F( // Edit the data, but include a conflicting name, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Jane Doe"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -2763,8 +2756,8 @@ TEST_F( // Edit the data, but don't include a name, and submit. credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); // With the offer-to-save decision deferred to Google Payments, Payments can @@ -2832,8 +2825,8 @@ TEST_F( // Edit the data, include a expiration date, and submit this time. credit_card_form.fields[0].value = ASCIIToUTF16("Jane Doe"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); FormSubmitted(credit_card_form); @@ -3053,7 +3046,7 @@ TEST_F(CreditCardSaveManagerTest, credit_card_form.fields[0].value = ASCIIToUTF16("John Smith"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); credit_card_form.fields[2].value = ASCIIToUTF16(""); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -3095,7 +3088,7 @@ TEST_F(CreditCardSaveManagerTest, // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("John Smith"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); credit_card_form.fields[3].value = ASCIIToUTF16(""); credit_card_form.fields[4].value = ASCIIToUTF16("123"); @@ -3226,8 +3219,8 @@ TEST_F(CreditCardSaveManagerTest, // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -3269,8 +3262,8 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_UploadDetailsFails) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -3302,7 +3295,8 @@ TEST_F(CreditCardSaveManagerTest, DuplicateMaskedCreditCard_NoUpload) { // enter below. CreditCard credit_card(CreditCard::MASKED_SERVER_CARD, "a123"); test::SetCreditCardInfo(&credit_card, "Flo Master", "1111", - NextMonth().c_str(), NextYear().c_str(), "1"); + test::NextMonth().c_str(), test::NextYear().c_str(), + "1"); credit_card.SetNetworkForMaskedCard(kVisaCard); personal_data_.AddServerCreditCard(credit_card); @@ -3314,8 +3308,8 @@ TEST_F(CreditCardSaveManagerTest, DuplicateMaskedCreditCard_NoUpload) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); // Local save prompt should not be shown as there is alredy masked @@ -3334,8 +3328,8 @@ TEST_F(CreditCardSaveManagerTest, NothingIfNothingFound) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16(""); // No name set credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16(""); // No CVC set // Submit the form and check what detected_values for an upload save would be. @@ -3364,8 +3358,8 @@ TEST_F(CreditCardSaveManagerTest, DetectCvc) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16(""); // No name set credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); // Submit the form and ensure the detected_values for an upload save contained @@ -3386,8 +3380,8 @@ TEST_F(CreditCardSaveManagerTest, DetectCardholderName) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("John Smith"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16(""); // No CVC set // Submit the form and ensure the detected_values for an upload save contained @@ -3415,8 +3409,8 @@ TEST_F(CreditCardSaveManagerTest, DetectAddressName) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16(""); // No name set credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16(""); // No CVC set // Submit the form and ensure the detected_values for an upload save contained @@ -3444,8 +3438,8 @@ TEST_F(CreditCardSaveManagerTest, DetectCardholderAndAddressNameIfMatching) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("John Smith"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16(""); // No CVC set // Submit the form and ensure the detected_values for an upload save contained @@ -3474,8 +3468,8 @@ TEST_F(CreditCardSaveManagerTest, DetectNoUniqueNameIfNamesConflict) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Miles Prower"); // Conflict! credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16(""); // No CVC set // Submit the form and check what detected_values for an upload save would be. @@ -3502,8 +3496,8 @@ TEST_F(CreditCardSaveManagerTest, DetectPostalCode) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16(""); // No name set credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16(""); // No CVC set // Submit the form and ensure the detected_values for an upload save contained @@ -3535,8 +3529,8 @@ TEST_F(CreditCardSaveManagerTest, DetectNoUniquePostalCodeIfZipsConflict) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16(""); // No name set credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16(""); // No CVC set // Submit the form and check what detected_values for an upload save would be. @@ -3560,8 +3554,8 @@ TEST_F(CreditCardSaveManagerTest, DetectAddressLine) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16(""); // No name set credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16(""); // No CVC set // Submit the form and ensure the detected_values for an upload save contained @@ -3589,8 +3583,8 @@ TEST_F(CreditCardSaveManagerTest, DetectLocality) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16(""); // No name set credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16(""); // No CVC set // Submit the form and ensure the detected_values for an upload save contained @@ -3617,8 +3611,8 @@ TEST_F(CreditCardSaveManagerTest, DetectAdministrativeArea) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16(""); // No name set credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16(""); // No CVC set // Submit the form and ensure the detected_values for an upload save contained @@ -3646,8 +3640,8 @@ TEST_F(CreditCardSaveManagerTest, DetectCountryCode) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16(""); // No name set credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16(""); // No CVC set // Submit the form and ensure the detected_values for an upload save contained @@ -3674,8 +3668,8 @@ TEST_F(CreditCardSaveManagerTest, DetectHasGooglePaymentAccount) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16(""); // No name set credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16(""); // No CVC set // Submit the form and ensure the detected_values for an upload save contained @@ -3708,8 +3702,8 @@ TEST_F(CreditCardSaveManagerTest, DetectEverythingAtOnce) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("John Smith"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); // Submit the form and ensure the detected_values for an upload save contained @@ -3747,8 +3741,8 @@ TEST_F(CreditCardSaveManagerTest, DetectSubsetOfPossibleFields) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Miles Prower"); // Conflict! credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); // Submit the form and ensure the detected_values for an upload save contained @@ -3795,8 +3789,8 @@ TEST_F(CreditCardSaveManagerTest, DetectAddressComponentsAcrossProfiles) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16(""); // No name set credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16(""); // No CVC set // Submit the form and ensure the detected_values for an upload save contained @@ -3835,8 +3829,8 @@ TEST_F(CreditCardSaveManagerTest, // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16(""); // No name! credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16(""); // No CVC! base::HistogramTester histogram_tester; @@ -3895,8 +3889,8 @@ TEST_F( // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("John Smith"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -3938,8 +3932,8 @@ TEST_F( credit_card_form.fields[0].value = ASCIIToUTF16("John"); credit_card_form.fields[1].value = ASCIIToUTF16("Smith"); credit_card_form.fields[2].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[3].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[4].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[4].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[5].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -3983,8 +3977,8 @@ TEST_F( // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16(""); // No name! credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16(""); // No CVC! base::HistogramTester histogram_tester; @@ -4014,8 +4008,8 @@ TEST_F(CreditCardSaveManagerTest, // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16(""); // No CVC! base::HistogramTester histogram_tester; @@ -4057,8 +4051,8 @@ TEST_F(CreditCardSaveManagerTest, // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16(""); // No name! credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -4106,8 +4100,8 @@ TEST_F(CreditCardSaveManagerTest, // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Miles Prower"); // Conflict! credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -4157,8 +4151,8 @@ TEST_F(CreditCardSaveManagerTest, // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -4213,8 +4207,8 @@ TEST_F(CreditCardSaveManagerTest, // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -4256,8 +4250,8 @@ TEST_F(CreditCardSaveManagerTest, // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16(""); // No name! credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16(""); // No CVC! base::HistogramTester histogram_tester; @@ -4297,7 +4291,8 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_UploadOfLocalCard) { // enter below. CreditCard local_card; test::SetCreditCardInfo(&local_card, "Flo Master", "4111111111111111", - NextMonth().c_str(), NextYear().c_str(), "1"); + test::NextMonth().c_str(), test::NextYear().c_str(), + "1"); local_card.set_record_type(CreditCard::LOCAL_CARD); personal_data_.AddCreditCard(local_card); @@ -4320,8 +4315,8 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_UploadOfLocalCard) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -4360,8 +4355,8 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_UploadOfNewCard) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -4392,7 +4387,8 @@ TEST_F(CreditCardSaveManagerTest, // enter below. CreditCard local_card; test::SetCreditCardInfo(&local_card, "Flo Master", "4111111111111111", - NextMonth().c_str(), NextYear().c_str(), "1"); + test::NextMonth().c_str(), test::NextYear().c_str(), + "1"); local_card.set_record_type(CreditCard::LOCAL_CARD); personal_data_.AddCreditCard(local_card); @@ -4415,8 +4411,8 @@ TEST_F(CreditCardSaveManagerTest, // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -4450,8 +4446,8 @@ TEST_F(CreditCardSaveManagerTest, // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); // Confirm that upload happened and that no experiment flag state was sent in @@ -4480,8 +4476,8 @@ TEST_F(CreditCardSaveManagerTest, // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); // Confirm that the preflight request contained @@ -4509,8 +4505,8 @@ TEST_F(CreditCardSaveManagerTest, // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); // Confirm that the preflight request contained the correct UploadCardSource. @@ -4540,8 +4536,8 @@ TEST_F(CreditCardSaveManagerTest, // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -4589,8 +4585,8 @@ TEST_F(CreditCardSaveManagerTest, // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -4631,8 +4627,8 @@ TEST_F(CreditCardSaveManagerTest, // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -4678,8 +4674,8 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_MaxStrikesDisallowsSave) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -4725,8 +4721,8 @@ TEST_F(CreditCardSaveManagerTest, // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -4776,8 +4772,8 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_MaxStrikesStillAllowsSave) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -4817,8 +4813,8 @@ TEST_F(CreditCardSaveManagerTest, LocallySaveCreditCard_ClearStrikesOnAdd) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); FormSubmitted(credit_card_form); @@ -4858,8 +4854,8 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_ClearStrikesOnAdd) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); FormSubmitted(credit_card_form); @@ -4891,8 +4887,8 @@ TEST_F(CreditCardSaveManagerTest, LocallySaveCreditCard_NumStrikesLoggedOnAdd) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -4936,8 +4932,8 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NumStrikesLoggedOnAdd) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -5018,8 +5014,8 @@ TEST_F(CreditCardSaveManagerTest, credit_card_form.fields[0].value = ASCIIToUTF16("Flo"); credit_card_form.fields[1].value = ASCIIToUTF16("Master"); credit_card_form.fields[2].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[3].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[4].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[4].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[5].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -5069,8 +5065,8 @@ TEST_F(CreditCardSaveManagerTest, credit_card_form.fields[0].value = ASCIIToUTF16("Flo"); credit_card_form.fields[1].value = ASCIIToUTF16("Master"); credit_card_form.fields[2].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[3].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[4].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[4].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[5].value = ASCIIToUTF16("123"); base::HistogramTester histogram_tester; @@ -5098,8 +5094,8 @@ TEST_F(CreditCardSaveManagerTest, UploadSaveNotOfferedForUnsupportedCard) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("5454545454545454"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); // Since card isn't in any of the supported ranges, local save should be @@ -5125,15 +5121,16 @@ TEST_F(CreditCardSaveManagerTest, LocalSaveNotOfferedForSavedUnsupportedCard) { // enter below. CreditCard local_card; test::SetCreditCardInfo(&local_card, "Flo Master", "5454545454545454", - NextMonth().c_str(), NextYear().c_str(), "1"); + test::NextMonth().c_str(), test::NextYear().c_str(), + "1"); local_card.set_record_type(CreditCard::LOCAL_CARD); personal_data_.AddCreditCard(local_card); // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("5454545454545454"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); // Since card is already saved, local save should not be offered. @@ -5157,8 +5154,8 @@ TEST_F(CreditCardSaveManagerTest, UploadSaveOfferedForSupportedCard) { // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); - credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth()); - credit_card_form.fields[3].value = ASCIIToUTF16(NextYear()); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); credit_card_form.fields[4].value = ASCIIToUTF16("123"); // Since card is in one of the supported ranges(4111-4113), upload save should @@ -5168,4 +5165,40 @@ TEST_F(CreditCardSaveManagerTest, UploadSaveOfferedForSupportedCard) { EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded()); } +// Tests that if payment client returns an invalid legal message upload should +// not be offered. +TEST_F(CreditCardSaveManagerTest, InvalidLegalMessageInOnDidGetUploadDetails) { + payments_client_->SetUseInvalidLegalMessageInGetUploadDetails(true); + + // Create, fill and submit an address form in order to establish a recent + // profile which can be selected for the upload request. + FormData address_form; + test::CreateTestAddressFormData(&address_form); + FormsSeen(std::vector<FormData>(1, address_form)); + + ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + FormSubmitted(address_form); + + // Set up our credit card form data. + FormData credit_card_form; + CreateTestCreditCardFormData(&credit_card_form, CreditCardFormOptions()); + FormsSeen(std::vector<FormData>(1, credit_card_form)); + + // Edit the data, and submit. + const char* const card_number = "4111111111111111"; + credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); + credit_card_form.fields[1].value = ASCIIToUTF16(card_number); + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); + credit_card_form.fields[4].value = ASCIIToUTF16("123"); + + base::HistogramTester histogram_tester; + FormSubmitted(credit_card_form); + + // Verify that the correct histogram entries were logged. + ExpectCardUploadDecision( + histogram_tester, + AutofillMetrics::UPLOAD_NOT_OFFERED_INVALID_LEGAL_MESSAGE); +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/payments/fido_authentication_strike_database.cc b/chromium/components/autofill/core/browser/payments/fido_authentication_strike_database.cc new file mode 100644 index 00000000000..2a1125b55b0 --- /dev/null +++ b/chromium/components/autofill/core/browser/payments/fido_authentication_strike_database.cc @@ -0,0 +1,42 @@ +// Copyright 2019 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 "components/autofill/core/browser/payments/fido_authentication_strike_database.h" + +#include "components/autofill/core/browser/proto/strike_data.pb.h" + +namespace autofill { + +const int + FidoAuthenticationStrikeDatabase::kStrikesToAddWhenOptInOfferDeclined = 1; +const int FidoAuthenticationStrikeDatabase:: + kStrikesToAddWhenUserVerificationFailsOnOptInAttempt = 2; +const int FidoAuthenticationStrikeDatabase::kStrikesToAddWhenUserOptsOut = 3; + +FidoAuthenticationStrikeDatabase::FidoAuthenticationStrikeDatabase( + StrikeDatabase* strike_database) + : StrikeDatabaseIntegratorBase(strike_database) { + RemoveExpiredStrikes(); +} + +FidoAuthenticationStrikeDatabase::~FidoAuthenticationStrikeDatabase() {} + +std::string FidoAuthenticationStrikeDatabase::GetProjectPrefix() { + return "FidoAuthentication"; +} + +int FidoAuthenticationStrikeDatabase::GetMaxStrikesLimit() { + return 3; +} + +long long FidoAuthenticationStrikeDatabase::GetExpiryTimeMicros() { + // Expiry time is six months. + return 1000000LL * 60 * 60 * 24 * 30 * 6; +} + +bool FidoAuthenticationStrikeDatabase::UniqueIdsRequired() { + return false; +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/payments/fido_authentication_strike_database.h b/chromium/components/autofill/core/browser/payments/fido_authentication_strike_database.h new file mode 100644 index 00000000000..f06f9fc5ebe --- /dev/null +++ b/chromium/components/autofill/core/browser/payments/fido_authentication_strike_database.h @@ -0,0 +1,38 @@ +// Copyright 2019 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. + +#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_FIDO_AUTHENTICATION_STRIKE_DATABASE_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_FIDO_AUTHENTICATION_STRIKE_DATABASE_H_ + +#include <string> + +#include "components/autofill/core/browser/payments/strike_database.h" +#include "components/autofill/core/browser/payments/strike_database_integrator_base.h" + +namespace autofill { + +// Implementation of StrikeDatabaseIntegratorBase for offering FIDO +// authentication for card unmasking. +class FidoAuthenticationStrikeDatabase : public StrikeDatabaseIntegratorBase { + public: + FidoAuthenticationStrikeDatabase(StrikeDatabase* strike_database); + ~FidoAuthenticationStrikeDatabase() override; + + // Strikes to add when user declines opt-in offer. + static const int kStrikesToAddWhenOptInOfferDeclined; + // Strikes to add when user fails to complete user-verification for an opt-in + // attempt. + static const int kStrikesToAddWhenUserVerificationFailsOnOptInAttempt; + // Strikes to add when user opts-out from settings page. + static const int kStrikesToAddWhenUserOptsOut; + + std::string GetProjectPrefix() override; + int GetMaxStrikesLimit() override; + long long GetExpiryTimeMicros() override; + bool UniqueIdsRequired() override; +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_FIDO_AUTHENTICATION_STRIKE_DATABASE_H_ diff --git a/chromium/components/autofill/core/browser/payments/full_card_request.cc b/chromium/components/autofill/core/browser/payments/full_card_request.cc index 2d8be66ebc5..2c87a60b6df 100644 --- a/chromium/components/autofill/core/browser/payments/full_card_request.cc +++ b/chromium/components/autofill/core/browser/payments/full_card_request.cc @@ -13,6 +13,8 @@ #include "components/autofill/core/browser/payments/payments_util.h" #include "components/autofill/core/browser/personal_data_manager.h" #include "components/autofill/core/common/autofill_clock.h" +#include "components/autofill/core/common/autofill_payments_features.h" +#include "components/autofill/core/common/autofill_tick_clock.h" namespace autofill { namespace payments { @@ -110,10 +112,6 @@ void FullCardRequest::GetFullCard(const CreditCard& card, } } -bool FullCardRequest::IsGettingFullCard() const { - return !!request_; -} - void FullCardRequest::OnUnmaskPromptAccepted( const UserProvidedUnmaskDetails& user_response) { if (!user_response.exp_month.empty()) @@ -155,12 +153,13 @@ void FullCardRequest::OnUnmaskPromptClosed() { void FullCardRequest::OnDidGetUnmaskRiskData(const std::string& risk_data) { request_->risk_data = risk_data; if (!request_->user_response.cvc.empty() || - !request_->fido_assertion_info.is_none()) + !request_->fido_assertion_info.is_none()) { SendUnmaskCardRequest(); + } } void FullCardRequest::SendUnmaskCardRequest() { - real_pan_request_timestamp_ = AutofillClock::Now(); + real_pan_request_timestamp_ = AutofillTickClock::NowTicks(); payments_client_->UnmaskCard(*request_, base::BindOnce(&FullCardRequest::OnDidGetRealPan, weak_ptr_factory_.GetWeakPtr())); @@ -169,8 +168,18 @@ void FullCardRequest::SendUnmaskCardRequest() { void FullCardRequest::OnDidGetRealPan( AutofillClient::PaymentsRpcResult result, payments::PaymentsClient::UnmaskResponseDetails& response_details) { - AutofillMetrics::LogRealPanDuration( - AutofillClock::Now() - real_pan_request_timestamp_, result); + // If the CVC field is populated, that means the user performed a CVC check. + // If FIDO AssertionInfo is populated, then the user must have performed FIDO + // authentication. Exactly one of these fields must be populated. + DCHECK_NE(request_->user_response.cvc.empty(), + request_->fido_assertion_info.is_none()); + if (!request_->user_response.cvc.empty()) { + AutofillMetrics::LogRealPanDuration( + AutofillTickClock::NowTicks() - real_pan_request_timestamp_, result); + } else if (!request_->fido_assertion_info.is_none()) { + AutofillMetrics::LogCardUnmaskDurationAfterWebauthn( + AutofillTickClock::NowTicks() - real_pan_request_timestamp_, result); + } if (ui_delegate_) ui_delegate_->OnUnmaskVerificationResult(result); @@ -204,9 +213,16 @@ void FullCardRequest::OnDidGetRealPan( // |response_details_| if |user_response.fido_opt_in| was not set to true // to avoid an unwanted registration prompt. unmask_response_details_ = response_details; + + const base::string16 cvc = + base::FeatureList::IsEnabled( + features::kAutofillAlwaysReturnCloudTokenizedCard) && + !response_details.dcvv.empty() + ? base::UTF8ToUTF16(response_details.dcvv) + : request_->user_response.cvc; if (result_delegate_) - result_delegate_->OnFullCardRequestSucceeded( - *this, request_->card, request_->user_response.cvc); + result_delegate_->OnFullCardRequestSucceeded(*this, request_->card, + cvc); Reset(); break; } diff --git a/chromium/components/autofill/core/browser/payments/full_card_request.h b/chromium/components/autofill/core/browser/payments/full_card_request.h index 9d5338b3c3d..c8608085877 100644 --- a/chromium/components/autofill/core/browser/payments/full_card_request.h +++ b/chromium/components/autofill/core/browser/payments/full_card_request.h @@ -18,6 +18,10 @@ namespace autofill { +class AutofillManagerTest; +class AutofillMetricsTest; +class CreditCardAccessManagerTest; +class CreditCardCVCAuthenticatorTest; class CreditCard; class PersonalDataManager; @@ -85,9 +89,6 @@ class FullCardRequest final : public CardUnmaskDelegate { base::WeakPtr<ResultDelegate> result_delegate, base::Value fido_assertion_info); - // Returns true if there's a pending request to get the full card. - bool IsGettingFullCard() const; - // Called by the payments client when a card has been unmasked. void OnDidGetRealPan( AutofillClient::PaymentsRpcResult result, @@ -103,6 +104,11 @@ class FullCardRequest final : public CardUnmaskDelegate { } private: + friend class autofill::AutofillManagerTest; + friend class autofill::AutofillMetricsTest; + friend class autofill::CreditCardAccessManagerTest; + friend class autofill::CreditCardCVCAuthenticatorTest; + // Retrieves the pan for |card| and invokes // Delegate::OnFullCardRequestSucceeded() or // Delegate::OnFullCardRequestFailed(). Only one request should be active at a @@ -158,7 +164,7 @@ class FullCardRequest final : public CardUnmaskDelegate { // The timestamp when the full PAN was requested from a server. For // histograms. - base::Time real_pan_request_timestamp_; + base::TimeTicks real_pan_request_timestamp_; // The timestamp when the form is parsed. For histograms. base::TimeTicks form_parsed_timestamp_; diff --git a/chromium/components/autofill/core/browser/payments/full_card_request_unittest.cc b/chromium/components/autofill/core/browser/payments/full_card_request_unittest.cc index f4fb8e536a2..d57e76c31c4 100644 --- a/chromium/components/autofill/core/browser/payments/full_card_request_unittest.cc +++ b/chromium/components/autofill/core/browser/payments/full_card_request_unittest.cc @@ -8,6 +8,7 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/strings/stringprintf.h" +#include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" @@ -18,6 +19,8 @@ #include "components/autofill/core/browser/test_autofill_client.h" #include "components/autofill/core/browser/test_autofill_driver.h" #include "components/autofill/core/browser/test_personal_data_manager.h" +#include "components/autofill/core/common/autofill_clock.h" +#include "components/autofill/core/common/autofill_payments_features.h" #include "net/url_request/url_request_test_util.h" #include "services/network/public/cpp/shared_url_loader_factory.h" #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" @@ -106,6 +109,17 @@ class FullCardRequestTest : public testing::Test { request_->OnDidGetRealPan(result, response.with_real_pan(real_pan)); } + void OnDidGetRealPanWithDcvv(AutofillClient::PaymentsRpcResult result, + const std::string& real_pan, + const std::string& dcvv) { + payments::PaymentsClient::UnmaskResponseDetails response; + request_->OnDidGetRealPan(result, + response.with_real_pan(real_pan).with_dcvv(dcvv)); + } + + protected: + base::test::ScopedFeatureList scoped_feature_list_; + private: base::test::SingleThreadTaskEnvironment task_environment_; MockPersonalDataManager personal_data_; @@ -159,6 +173,56 @@ TEST_F(FullCardRequestTest, GetFullCardPanAndCvcForMaskedServerCardViaCvc) { card_unmask_delegate()->OnUnmaskPromptClosed(); } +// Verify getting the full PAN and the dCVV for a masked server card when cloud +// tokenization is enabled. +TEST_F(FullCardRequestTest, GetFullCardPanAndDcvvForMaskedServerCardViaDcvv) { + scoped_feature_list_.InitAndEnableFeature( + features::kAutofillAlwaysReturnCloudTokenizedCard); + EXPECT_CALL(*result_delegate(), + OnFullCardRequestSucceeded( + testing::Ref(*request()), + CardMatches(CreditCard::FULL_SERVER_CARD, "4111"), + base::ASCIIToUTF16("321"))); + EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _)); + EXPECT_CALL(*ui_delegate(), + OnUnmaskVerificationResult(AutofillClient::SUCCESS)); + + request()->GetFullCard( + CreditCard(CreditCard::MASKED_SERVER_CARD, "server_id"), + AutofillClient::UNMASK_FOR_AUTOFILL, result_delegate()->AsWeakPtr(), + ui_delegate()->AsWeakPtr()); + CardUnmaskDelegate::UserProvidedUnmaskDetails details; + details.cvc = base::ASCIIToUTF16("123"); + card_unmask_delegate()->OnUnmaskPromptAccepted(details); + OnDidGetRealPanWithDcvv(AutofillClient::SUCCESS, "4111", "321"); + card_unmask_delegate()->OnUnmaskPromptClosed(); +} + +// Verify getting the full PAN for a masked server card when cloud +// tokenization is enabled but no dCVV is returned. +TEST_F(FullCardRequestTest, GetFullCardPanForMaskedServerCardWithoutDcvv) { + scoped_feature_list_.InitAndEnableFeature( + features::kAutofillAlwaysReturnCloudTokenizedCard); + EXPECT_CALL(*result_delegate(), + OnFullCardRequestSucceeded( + testing::Ref(*request()), + CardMatches(CreditCard::FULL_SERVER_CARD, "4111"), + base::ASCIIToUTF16("123"))); + EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _)); + EXPECT_CALL(*ui_delegate(), + OnUnmaskVerificationResult(AutofillClient::SUCCESS)); + + request()->GetFullCard( + CreditCard(CreditCard::MASKED_SERVER_CARD, "server_id"), + AutofillClient::UNMASK_FOR_AUTOFILL, result_delegate()->AsWeakPtr(), + ui_delegate()->AsWeakPtr()); + CardUnmaskDelegate::UserProvidedUnmaskDetails details; + details.cvc = base::ASCIIToUTF16("123"); + card_unmask_delegate()->OnUnmaskPromptAccepted(details); + OnDidGetRealPan(AutofillClient::SUCCESS, "4111"); + card_unmask_delegate()->OnUnmaskPromptClosed(); +} + // Verify getting the full PAN for a masked server card. TEST_F(FullCardRequestTest, GetFullCardPanAndCvcForMaskedServerCardViaFido) { EXPECT_CALL(*result_delegate(), @@ -263,7 +327,7 @@ TEST_F(FullCardRequestTest, GetFullCardPanAndCvcForExpiredFullServerCard) { OnUnmaskVerificationResult(AutofillClient::SUCCESS)); base::Time::Exploded today; - base::Time::Now().LocalExplode(&today); + AutofillClock::Now().LocalExplode(&today); CreditCard full_server_card(CreditCard::FULL_SERVER_CARD, "server_id"); test::SetCreditCardInfo(&full_server_card, nullptr, "4111", "12", base::StringPrintf("%d", today.year - 1).c_str(), @@ -493,7 +557,7 @@ TEST_F(FullCardRequestTest, UpdateExpDateForLocalCard) { OnUnmaskVerificationResult(AutofillClient::SUCCESS)); base::Time::Exploded today; - base::Time::Now().LocalExplode(&today); + AutofillClock::Now().LocalExplode(&today); CreditCard card; test::SetCreditCardInfo(&card, nullptr, "4111", "10", base::StringPrintf("%d", today.year - 1).c_str(), @@ -559,74 +623,5 @@ TEST_F(FullCardRequestTest, UnmaskForPaymentRequest) { card_unmask_delegate()->OnUnmaskPromptClosed(); } -// Verify that FullCardRequest::IsGettingFullCard() is true until the server -// returns the full PAN for a masked card. -TEST_F(FullCardRequestTest, IsGettingFullCardForMaskedServerCard) { - EXPECT_CALL(*result_delegate(), - OnFullCardRequestSucceeded( - testing::Ref(*request()), - CardMatches(CreditCard::FULL_SERVER_CARD, "4111"), - base::ASCIIToUTF16("123"))); - EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _)); - EXPECT_CALL(*ui_delegate(), - OnUnmaskVerificationResult(AutofillClient::SUCCESS)); - - EXPECT_FALSE(request()->IsGettingFullCard()); - - request()->GetFullCard( - CreditCard(CreditCard::MASKED_SERVER_CARD, "server_id"), - AutofillClient::UNMASK_FOR_AUTOFILL, result_delegate()->AsWeakPtr(), - ui_delegate()->AsWeakPtr()); - - EXPECT_TRUE(request()->IsGettingFullCard()); - - CardUnmaskDelegate::UserProvidedUnmaskDetails details; - details.cvc = base::ASCIIToUTF16("123"); - card_unmask_delegate()->OnUnmaskPromptAccepted(details); - - EXPECT_TRUE(request()->IsGettingFullCard()); - - OnDidGetRealPan(AutofillClient::SUCCESS, "4111"); - - EXPECT_FALSE(request()->IsGettingFullCard()); - - card_unmask_delegate()->OnUnmaskPromptClosed(); - - EXPECT_FALSE(request()->IsGettingFullCard()); -} - -// Verify that FullCardRequest::IsGettingFullCard() is true until the user types -// in the CVC for a card that is not masked. -TEST_F(FullCardRequestTest, IsGettingFullCardForLocalCard) { - EXPECT_CALL( - *result_delegate(), - OnFullCardRequestSucceeded(testing::Ref(*request()), - CardMatches(CreditCard::LOCAL_CARD, "4111"), - base::ASCIIToUTF16("123"))); - EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _)); - EXPECT_CALL(*ui_delegate(), - OnUnmaskVerificationResult(AutofillClient::SUCCESS)); - - EXPECT_FALSE(request()->IsGettingFullCard()); - - CreditCard card; - test::SetCreditCardInfo(&card, nullptr, "4111", "12", "2050", "1"); - request()->GetFullCard(card, AutofillClient::UNMASK_FOR_AUTOFILL, - result_delegate()->AsWeakPtr(), - ui_delegate()->AsWeakPtr()); - - EXPECT_TRUE(request()->IsGettingFullCard()); - - CardUnmaskDelegate::UserProvidedUnmaskDetails details; - details.cvc = base::ASCIIToUTF16("123"); - card_unmask_delegate()->OnUnmaskPromptAccepted(details); - - EXPECT_FALSE(request()->IsGettingFullCard()); - - card_unmask_delegate()->OnUnmaskPromptClosed(); - - EXPECT_FALSE(request()->IsGettingFullCard()); -} - } // namespace payments } // namespace autofill diff --git a/chromium/components/autofill/core/browser/payments/legal_message_line.cc b/chromium/components/autofill/core/browser/payments/legal_message_line.cc index b18bc273c41..34baff793d9 100644 --- a/chromium/components/autofill/core/browser/payments/legal_message_line.cc +++ b/chromium/components/autofill/core/browser/payments/legal_message_line.cc @@ -113,7 +113,7 @@ bool LegalMessageLine::ParseLine(const base::Value& line, const base::Value* template_parameters = line.FindKeyOfType("template_parameter", base::Value::Type::LIST); if (template_parameters) { - const base::Value::ListStorage& template_parameters_storage = + base::span<const base::Value> template_parameters_storage = template_parameters->GetList(); display_texts.reserve(template_parameters_storage.size()); links_.reserve(template_parameters_storage.size()); diff --git a/chromium/components/autofill/core/browser/payments/local_card_migration_manager.cc b/chromium/components/autofill/core/browser/payments/local_card_migration_manager.cc index f3dae8c54d6..85556ad2c5d 100644 --- a/chromium/components/autofill/core/browser/payments/local_card_migration_manager.cc +++ b/chromium/components/autofill/core/browser/payments/local_card_migration_manager.cc @@ -5,8 +5,8 @@ #include "components/autofill/core/browser/payments/local_card_migration_manager.h" #include <stddef.h> - #include <algorithm> +#include <unordered_map> #include <vector> #include "base/bind.h" @@ -228,9 +228,19 @@ void LocalCardMigrationManager::OnDidGetUploadDetails( observer_for_testing_->OnReceivedGetUploadDetailsResponse(); if (result == AutofillClient::SUCCESS) { + LegalMessageLine::Parse(*legal_message, &legal_message_lines_, + /*escape_apostrophes=*/true); + + if (legal_message_lines_.empty()) { + AutofillMetrics::LogLocalCardMigrationDecisionMetric( + AutofillMetrics::LocalCardMigrationDecisionMetric:: + NOT_OFFERED_INVALID_LEGAL_MESSAGE); + return; + } + migration_request_.context_token = context_token; - legal_message_ = base::DictionaryValue::From(std::move(legal_message)); migration_request_.risk_data.clear(); + // If we successfully received the legal docs, trigger the offer-to-migrate // dialog. If triggered from settings page, we pop-up the main prompt // directly. If not, we pop up the intermediate bubble. @@ -274,6 +284,7 @@ void LocalCardMigrationManager::OnDidGetUploadDetails( local_card_migration_origin_, AutofillMetrics::INTERMEDIATE_BUBBLE_SHOWN); } + // TODO(crbug.com/876895): Clean up the LoadRiskData Bind/BindRepeating // usages client_->LoadRiskData(base::BindRepeating( @@ -330,6 +341,12 @@ void LocalCardMigrationManager::OnDidMigrateLocalCards( NOTREACHED(); } } + + // If at least one card was migrated, notifies the |personal_data_manager_|. + // PDM uses this information to update the avatar button UI. + if (!migrated_cards.empty()) + personal_data_manager_->OnCreditCardSaved(/*is_local_card=*/false); + // Remove cards that were successfully migrated from local storage. personal_data_manager_->DeleteLocalCreditCards(migrated_cards); } @@ -388,7 +405,7 @@ void LocalCardMigrationManager::ShowMainMigrationDialog() { local_card_migration_origin_, AutofillMetrics::MAIN_DIALOG_SHOWN); // Pops up a larger, modal dialog showing the local cards to be uploaded. client_->ConfirmMigrateLocalCardToCloud( - std::move(legal_message_), + legal_message_lines_, personal_data_manager_->GetAccountInfoForPaymentsServer().email, migratable_credit_cards_, base::BindOnce( diff --git a/chromium/components/autofill/core/browser/payments/local_card_migration_manager.h b/chromium/components/autofill/core/browser/payments/local_card_migration_manager.h index d717e199bd0..3f2c73c3720 100644 --- a/chromium/components/autofill/core/browser/payments/local_card_migration_manager.h +++ b/chromium/components/autofill/core/browser/payments/local_card_migration_manager.h @@ -7,12 +7,14 @@ #include <memory> #include <string> +#include <unordered_map> #include <utility> #include <vector> #include "base/strings/string16.h" #include "components/autofill/core/browser/autofill_client.h" #include "components/autofill/core/browser/autofill_metrics.h" +#include "components/autofill/core/browser/payments/legal_message_line.h" #include "components/autofill/core/browser/payments/local_card_migration_strike_database.h" #include "components/autofill/core/browser/payments/payments_client.h" @@ -209,7 +211,8 @@ class LocalCardMigrationManager { observer_for_testing_ = observer; } - std::unique_ptr<base::DictionaryValue> legal_message_; + // The parsed lines from the legal message return from GetUploadDetails. + LegalMessageLines legal_message_lines_; std::string app_locale_; diff --git a/chromium/components/autofill/core/browser/payments/local_card_migration_manager_unittest.cc b/chromium/components/autofill/core/browser/payments/local_card_migration_manager_unittest.cc index e1441ba421d..2a09894c0e3 100644 --- a/chromium/components/autofill/core/browser/payments/local_card_migration_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/payments/local_card_migration_manager_unittest.cc @@ -1541,4 +1541,19 @@ TEST_F(LocalCardMigrationManagerTest, AutofillMetrics::LocalCardMigrationDecisionMetric::OFFERED); } +// Tests that if payment client returns an invalid legal message migration +// should not be offered. +TEST_F(LocalCardMigrationManagerTest, + InvalidLegalMessageInOnDidGetUploadDetails) { + payments_client_->SetUseInvalidLegalMessageInGetUploadDetails(true); + + base::HistogramTester histogram_tester; + UseLocalCardWithOtherLocalCardsOnFile(); + + // Verify that the correct histogram entries were logged. + ExpectUniqueLocalCardMigrationDecision( + histogram_tester, AutofillMetrics::LocalCardMigrationDecisionMetric:: + NOT_OFFERED_INVALID_LEGAL_MESSAGE); +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/payments/payments_client.cc b/chromium/components/autofill/core/browser/payments/payments_client.cc index b097a05e500..a7e30b9dcd5 100644 --- a/chromium/components/autofill/core/browser/payments/payments_client.cc +++ b/chromium/components/autofill/core/browser/payments/payments_client.cc @@ -56,9 +56,11 @@ const char kUnmaskCardRequestPath[] = const char kUnmaskCardRequestFormat[] = "requestContentType=application/json; charset=utf-8&request=%s" "&s7e_13_cvc=%s"; +const char kUnmaskCardRequestFormatWithoutCvc[] = + "requestContentType=application/json; charset=utf-8&request=%s"; const char kOptChangeRequestPath[] = - "payments/apis/chromepaymentsservice/autofillauthoptchange"; + "payments/apis/chromepaymentsservice/updateautofilluserpreference"; const char kGetUploadDetailsRequestPath[] = "payments/apis/chromepaymentsservice/getdetailsforsavecard"; @@ -142,7 +144,7 @@ void AppendStringIfNotEmpty(const AutofillProfile& profile, base::Value& list) { const base::string16 value = profile.GetInfo(type, app_locale); if (!value.empty()) - list.GetList().emplace_back(value); + list.Append(value); } // Returns a dictionary with the structure expected by Payments RPCs, containing @@ -228,7 +230,7 @@ void SetActiveExperiments(const std::vector<const char*>& active_experiments, base::Value active_chrome_experiments(base::Value::Type::LIST); for (const char* it : active_experiments) - active_chrome_experiments.GetList().emplace_back(it); + active_chrome_experiments.Append(it); request_dict.SetKey("active_chrome_experiments", std::move(active_chrome_experiments)); @@ -295,7 +297,7 @@ class GetUnmaskDetailsRequest : public PaymentsRequest { unmask_details_.fido_request_options = dictionary_value->Clone(); const auto* fido_eligible_card_ids = response.FindKeyOfType( - "fido_eligible_credit_card_id", base::Value::Type::LIST); + "fido_eligible_card_id", base::Value::Type::LIST); if (fido_eligible_card_ids) { for (const base::Value& result : fido_eligible_card_ids->GetList()) { unmask_details_.fido_eligible_card_ids.insert(result.GetString()); @@ -347,9 +349,14 @@ class UnmaskCardRequest : public PaymentsRequest { std::string GetRequestContent() override { base::Value request_dict(base::Value::Type::DICTIONARY); - request_dict.SetKey("encrypted_cvc", base::Value("__param:s7e_13_cvc")); request_dict.SetKey("credit_card_id", base::Value(request_details_.card.server_id())); + if (base::FeatureList::IsEnabled( + features::kAutofillAlwaysReturnCloudTokenizedCard)) { + // See b/140727361. + request_dict.SetKey("instrument_token", + base::Value("INSTRUMENT_TOKEN_FOR_TEST")); + } request_dict.SetKey("risk_data_encoded", BuildRiskDictionary(request_details_.risk_data)); base::Value context(base::Value::Type::DICTIONARY); @@ -380,19 +387,33 @@ class UnmaskCardRequest : public PaymentsRequest { "opt_in_fido_auth", base::Value(request_details_.user_response.enable_fido_auth)); - if (request_details_.fido_assertion_info.is_dict()) { + // Either FIDO assertion info is set or CVC is set, never both. + bool is_cvc_auth = !request_details_.user_response.cvc.empty(); + bool is_fido_auth = request_details_.fido_assertion_info.is_dict(); + + DCHECK_NE(is_cvc_auth, is_fido_auth); + if (is_cvc_auth) { + request_dict.SetKey("encrypted_cvc", base::Value("__param:s7e_13_cvc")); + } else { request_dict.SetKey("fido_assertion_info", std::move(request_details_.fido_assertion_info)); } std::string json_request; base::JSONWriter::Write(request_dict, &json_request); - std::string request_content = base::StringPrintf( - kUnmaskCardRequestFormat, - net::EscapeUrlEncodedData(json_request, true).c_str(), - net::EscapeUrlEncodedData( - base::UTF16ToASCII(request_details_.user_response.cvc), true) - .c_str()); + std::string request_content; + if (is_cvc_auth) { + request_content = base::StringPrintf( + kUnmaskCardRequestFormat, + net::EscapeUrlEncodedData(json_request, true).c_str(), + net::EscapeUrlEncodedData( + base::UTF16ToASCII(request_details_.user_response.cvc), true) + .c_str()); + } else { + request_content = base::StringPrintf( + kUnmaskCardRequestFormatWithoutCvc, + net::EscapeUrlEncodedData(json_request, true).c_str()); + } // Payments is reporting receiving blank or non-standard-length CVCs. // Log CVC length being sent to gauge how often this is happening. @@ -414,10 +435,21 @@ class UnmaskCardRequest : public PaymentsRequest { const auto* pan = response.FindStringKey("pan"); response_details_.real_pan = pan ? *pan : std::string(); + const auto* dcvv = response.FindStringKey("dcvv"); + response_details_.dcvv = dcvv ? *dcvv : std::string(); + const auto* creation_options = response.FindKeyOfType( "fido_creation_options", base::Value::Type::DICTIONARY); if (creation_options) response_details_.fido_creation_options = creation_options->Clone(); + + const auto* request_options = response.FindKeyOfType( + "fido_request_options", base::Value::Type::DICTIONARY); + if (request_options) + response_details_.fido_request_options = request_options->Clone(); + + const auto* token = response.FindStringKey("card_authorization_token"); + response_details_.card_authorization_token = token ? *token : std::string(); } bool IsResponseComplete() override { @@ -443,7 +475,9 @@ class OptChangeRequest : public PaymentsRequest { public: OptChangeRequest( const PaymentsClient::OptChangeRequestDetails& request_details, - OptChangeCallback callback, + base::OnceCallback<void(AutofillClient::PaymentsRpcResult, + PaymentsClient::OptChangeResponseDetails&)> + callback, const bool full_sync_enabled) : request_details_(request_details), callback_(std::move(callback)), @@ -470,46 +504,84 @@ class OptChangeRequest : public PaymentsRequest { std::move(chrome_user_context)); } - request_dict.SetKey("opt_in", base::Value(request_details_.opt_in)); + std::string reason; + switch (request_details_.reason) { + case PaymentsClient::OptChangeRequestDetails::ENABLE_FIDO_AUTH: + reason = "ENABLE_FIDO_AUTH"; + break; + case PaymentsClient::OptChangeRequestDetails::DISABLE_FIDO_AUTH: + reason = "DISABLE_FIDO_AUTH"; + break; + case PaymentsClient::OptChangeRequestDetails::ADD_CARD_FOR_FIDO_AUTH: + reason = "ADD_CARD_FOR_FIDO_AUTH"; + break; + default: + NOTREACHED(); + break; + } + request_dict.SetKey("reason", base::Value(reason)); if (request_details_.fido_authenticator_response.is_dict()) { - request_dict.SetKey( + base::Value fido_authentication_info(base::Value::Type::DICTIONARY); + + fido_authentication_info.SetKey( "fido_authenticator_response", std::move(request_details_.fido_authenticator_response)); + + if (!request_details_.card_authorization_token.empty()) { + fido_authentication_info.SetKey( + "card_authorization_token", + base::Value(request_details_.card_authorization_token)); + } + + request_dict.SetKey("fido_authentication_info", + std::move(fido_authentication_info)); } std::string request_content; base::JSONWriter::Write(request_dict, &request_content); - VLOG(3) << "autofillauthoptchange request body: " << request_content; + VLOG(3) << "updateautofilluserpreference request body: " << request_content; return request_content; } void ParseResponse(const base::Value& response) override { - const auto* user_is_opted_in = - response.FindKeyOfType("user_is_opted_in", base::Value::Type::BOOLEAN); - if (user_is_opted_in) - user_is_opted_in_ = user_is_opted_in->GetBool(); + const auto* fido_authentication_info = response.FindKeyOfType( + "fido_authentication_info", base::Value::Type::DICTIONARY); + if (!fido_authentication_info) + return; - const auto* fido_creation_options = response.FindKeyOfType( + const auto* user_status = + fido_authentication_info->FindStringKey("user_status"); + if (user_status && *user_status != "UNKNOWN_USER_STATUS") + response_details_.user_is_opted_in = + (*user_status == "FIDO_AUTH_ENABLED"); + + const auto* fido_creation_options = fido_authentication_info->FindKeyOfType( "fido_creation_options", base::Value::Type::DICTIONARY); if (fido_creation_options) - fido_creation_options_ = fido_creation_options->Clone(); + response_details_.fido_creation_options = fido_creation_options->Clone(); + + const auto* fido_request_options = fido_authentication_info->FindKeyOfType( + "fido_request_options", base::Value::Type::DICTIONARY); + if (fido_request_options) + response_details_.fido_request_options = fido_request_options->Clone(); } - bool IsResponseComplete() override { return user_is_opted_in_.has_value(); } + bool IsResponseComplete() override { + return response_details_.user_is_opted_in.has_value(); + } void RespondToDelegate(AutofillClient::PaymentsRpcResult result) override { - std::move(callback_).Run( - result, user_is_opted_in_.value_or(!request_details_.opt_in), - std::move(fido_creation_options_)); + std::move(callback_).Run(result, response_details_); } private: PaymentsClient::OptChangeRequestDetails request_details_; - OptChangeCallback callback_; + base::OnceCallback<void(AutofillClient::PaymentsRpcResult, + PaymentsClient::OptChangeResponseDetails&)> + callback_; const bool full_sync_enabled_; - base::Optional<bool> user_is_opted_in_; - base::Value fido_creation_options_; + PaymentsClient::OptChangeResponseDetails response_details_; DISALLOW_COPY_AND_ASSIGN(OptChangeRequest); }; @@ -568,8 +640,7 @@ class GetUploadDetailsRequest : public PaymentsRequest { // min address is not possible). The final parameter directs // BuildAddressDictionary to omit names and phone numbers, which aren't // useful for these purposes. - addresses.GetList().push_back( - BuildAddressDictionary(profile, app_locale_, false)); + addresses.Append(BuildAddressDictionary(profile, app_locale_, false)); } request_dict.SetKey("address", std::move(addresses)); @@ -740,8 +811,7 @@ class UploadCardRequest : public PaymentsRequest { base::Value addresses(base::Value::Type::LIST); for (const AutofillProfile& profile : request_details_.profiles) { - addresses.GetList().push_back( - BuildAddressDictionary(profile, app_locale, true)); + addresses.Append(BuildAddressDictionary(profile, app_locale, true)); } request_dict.SetKey("address", std::move(addresses)); @@ -859,7 +929,7 @@ class MigrateCardsRequest : public PaymentsRequest { for (size_t index = 0; index < migratable_credit_cards_.size(); ++index) { std::string pan_field_name = GetPanFieldName(index); // Generate credit card dictionary. - migrate_cards.GetList().push_back(BuildCreditCardDictionary( + migrate_cards.Append(BuildCreditCardDictionary( migratable_credit_cards_[index].credit_card(), app_locale, pan_field_name)); // Append pan data to the |all_pans_data|. @@ -968,6 +1038,12 @@ operator=(const PaymentsClient::UnmaskResponseDetails& other) { } else { fido_creation_options.reset(); } + if (other.fido_request_options.has_value()) { + fido_request_options = other.fido_request_options->Clone(); + } else { + fido_request_options.reset(); + } + card_authorization_token = other.card_authorization_token; return *this; } @@ -975,11 +1051,30 @@ PaymentsClient::OptChangeRequestDetails::OptChangeRequestDetails() {} PaymentsClient::OptChangeRequestDetails::OptChangeRequestDetails( const OptChangeRequestDetails& other) { app_locale = other.app_locale; - opt_in = other.opt_in; + reason = other.reason; fido_authenticator_response = other.fido_authenticator_response.Clone(); + card_authorization_token = other.card_authorization_token; } PaymentsClient::OptChangeRequestDetails::~OptChangeRequestDetails() {} +PaymentsClient::OptChangeResponseDetails::OptChangeResponseDetails() {} +PaymentsClient::OptChangeResponseDetails::OptChangeResponseDetails( + const OptChangeResponseDetails& other) { + user_is_opted_in = other.user_is_opted_in; + + if (other.fido_creation_options.has_value()) { + fido_creation_options = other.fido_creation_options->Clone(); + } else { + fido_creation_options.reset(); + } + if (other.fido_request_options.has_value()) { + fido_request_options = other.fido_request_options->Clone(); + } else { + fido_request_options.reset(); + } +} +PaymentsClient::OptChangeResponseDetails::~OptChangeResponseDetails() {} + PaymentsClient::UploadRequestDetails::UploadRequestDetails() {} PaymentsClient::UploadRequestDetails::UploadRequestDetails( const UploadRequestDetails& other) = default; @@ -1027,8 +1122,11 @@ void PaymentsClient::UnmaskCard( /*authenticate=*/true); } -void PaymentsClient::OptChange(const OptChangeRequestDetails request_details, - OptChangeCallback callback) { +void PaymentsClient::OptChange( + const OptChangeRequestDetails request_details, + base::OnceCallback<void(AutofillClient::PaymentsRpcResult, + PaymentsClient::OptChangeResponseDetails&)> + callback) { IssueRequest(std::make_unique<OptChangeRequest>( request_details, std::move(callback), account_info_getter_->IsSyncFeatureEnabled()), @@ -1231,7 +1329,7 @@ void PaymentsClient::StartTokenFetch(bool invalidate_old) { identity::ScopeSet payments_scopes; payments_scopes.insert(kPaymentsOAuth2Scope); - std::string account_id = + CoreAccountId account_id = account_info_getter_->GetAccountInfoForPaymentsServer().account_id; if (invalidate_old) { DCHECK(!access_token_.empty()); diff --git a/chromium/components/autofill/core/browser/payments/payments_client.h b/chromium/components/autofill/core/browser/payments/payments_client.h index 07523979cc2..9ce00b334e2 100644 --- a/chromium/components/autofill/core/browser/payments/payments_client.h +++ b/chromium/components/autofill/core/browser/payments/payments_client.h @@ -53,11 +53,6 @@ typedef base::OnceCallback<void(AutofillClient::PaymentsRpcResult, AutofillClient::UnmaskDetails&)> GetUnmaskDetailsCallback; -// Callback type for OptChange callback. -typedef base::OnceCallback< - void(AutofillClient::PaymentsRpcResult, bool, base::Value)> - OptChangeCallback; - // Billable service number is defined in Payments server to distinguish // different requests. const int kUnmaskCardBillableServiceNumber = 70154; @@ -106,8 +101,22 @@ class PaymentsClient { return *this; } + UnmaskResponseDetails& with_dcvv(std::string d) { + dcvv = d; + return *this; + } + std::string real_pan; - base::Optional<base::Value> fido_creation_options; + std::string dcvv; + // Challenge required for enrolling user into FIDO authentication for future + // card unmasking. + base::Optional<base::Value> fido_creation_options = base::nullopt; + // Challenge required for authorizing user for FIDO authentication for + // future card unmasking. + base::Optional<base::Value> fido_request_options = base::nullopt; + // An opaque token used to logically chain consecutive UnmaskCard and + // OptChange calls together. + std::string card_authorization_token = std::string(); }; // Information required to either opt-in or opt-out a user for FIDO @@ -118,8 +127,45 @@ class PaymentsClient { ~OptChangeRequestDetails(); std::string app_locale; - bool opt_in; + + // The reason for making the request. + enum Reason { + // Unknown default. + UNKNOWN_REASON = 0, + // The user wants to enable FIDO authentication for card unmasking. + ENABLE_FIDO_AUTH = 1, + // The user wants to disable FIDO authentication for card unmasking. + DISABLE_FIDO_AUTH = 2, + // The user is authorizing a new card for future FIDO authentication + // unmasking. + ADD_CARD_FOR_FIDO_AUTH = 3, + }; + + // Reason for the request. + Reason reason; + // Signature required for enrolling user into FIDO authentication for future + // card unmasking. base::Value fido_authenticator_response; + // An opaque token used to logically chain consecutive UnmaskCard and + // OptChange calls together. + std::string card_authorization_token = std::string(); + }; + + // Information retrieved from an OptChange request. + struct OptChangeResponseDetails { + OptChangeResponseDetails(); + OptChangeResponseDetails(const OptChangeResponseDetails& other); + ~OptChangeResponseDetails(); + + // Unset if response failed. True if user is opted-in for FIDO + // authentication for card unmasking. False otherwise. + base::Optional<bool> user_is_opted_in; + // Challenge required for enrolling user into FIDO authentication for future + // card unmasking. + base::Optional<base::Value> fido_creation_options; + // Challenge required for authorizing user for FIDO authentication for + // future card unmasking. + base::Optional<base::Value> fido_request_options; }; // A collection of the information required to make a credit card upload @@ -206,8 +252,11 @@ class PaymentsClient { // Opts-in or opts-out the user to use FIDO authentication for card unmasking // on this device. - void OptChange(const OptChangeRequestDetails request_details, - OptChangeCallback callback); + void OptChange( + const OptChangeRequestDetails request_details, + base::OnceCallback<void(AutofillClient::PaymentsRpcResult, + PaymentsClient::OptChangeResponseDetails&)> + callback); // Determine if the user meets the Payments service's conditions for upload. // The service uses |addresses| (from which names and phone numbers are diff --git a/chromium/components/autofill/core/browser/payments/payments_client_unittest.cc b/chromium/components/autofill/core/browser/payments/payments_client_unittest.cc index 39f0e812a59..10d0dfc4bbd 100644 --- a/chromium/components/autofill/core/browser/payments/payments_client_unittest.cc +++ b/chromium/components/autofill/core/browser/payments/payments_client_unittest.cc @@ -24,6 +24,7 @@ #include "components/autofill/core/browser/payments/local_card_migration_manager.h" #include "components/autofill/core/browser/payments/payments_client.h" #include "components/autofill/core/browser/test_personal_data_manager.h" +#include "components/autofill/core/common/autofill_clock.h" #include "components/autofill/core/common/autofill_features.h" #include "components/autofill/core/common/autofill_payments_features.h" #include "components/autofill/core/common/autofill_switches.h" @@ -140,13 +141,15 @@ class PaymentsClientTest : public testing::Test { unmask_response_details_ = &response; } - void OnDidGetOptChangeResult(AutofillClient::PaymentsRpcResult result, - bool user_is_opted_in, - base::Value fido_creation_options) { + void OnDidGetOptChangeResult( + AutofillClient::PaymentsRpcResult result, + PaymentsClient::OptChangeResponseDetails& response) { result_ = result; - user_is_opted_in_ = user_is_opted_in; - if (fido_creation_options.is_dict()) - fido_creation_options_ = fido_creation_options.Clone(); + opt_change_response_.user_is_opted_in = response.user_is_opted_in; + opt_change_response_.fido_creation_options = + std::move(response.fido_creation_options); + opt_change_response_.fido_request_options = + std::move(response.fido_request_options); } void OnDidGetUploadDetails( @@ -209,9 +212,10 @@ class PaymentsClientTest : public testing::Test { // If |opt_in| is set to true, then opts the user in to use FIDO // authentication for card unmasking. Otherwise opts the user out. - void StartOptChangeRequest(bool opt_in) { + void StartOptChangeRequest( + PaymentsClient::OptChangeRequestDetails::Reason reason) { PaymentsClient::OptChangeRequestDetails request_details; - request_details.opt_in = opt_in; + request_details.reason = reason; client_->OptChange( request_details, base::BindOnce(&PaymentsClientTest::OnDidGetOptChangeResult, @@ -279,7 +283,7 @@ class PaymentsClientTest : public testing::Test { void IssueOAuthToken() { identity_test_env_.WaitForAccessTokenRequestIfNecessaryAndRespondWithToken( "totally_real_token", - base::Time::Now() + base::TimeDelta::FromDays(10)); + AutofillClock::Now() + base::TimeDelta::FromDays(10)); // Verify the auth header. std::string auth_header_value; @@ -299,10 +303,8 @@ class PaymentsClientTest : public testing::Test { // Server ID of a saved card via credit card upload save. std::string server_id_; - // Status of the user's FIDO auth opt-in; returned from an OptChange call. - base::Optional<bool> user_is_opted_in_; - // FIDO auth enrollment creation options; returned from an OptChange call. - base::Value fido_creation_options_; + // The OptChangeResponseDetails retrieved from an OptChangeRequest. + PaymentsClient::OptChangeResponseDetails opt_change_response_; // The UnmaskResponseDetails retrieved from an UnmaskRequest. Includes PAN. PaymentsClient::UnmaskResponseDetails* unmask_response_details_ = nullptr; // The legal message returned from a GetDetails upload save preflight call. @@ -428,11 +430,13 @@ TEST_F(PaymentsClientTest, UnmaskSuccessViaFIDO) { TEST_F(PaymentsClientTest, UnmaskSuccessViaCVCWithCreationOptions) { StartUnmasking(CardUnmaskOptions().with_use_fido(false)); IssueOAuthToken(); - ReturnResponse(net::HTTP_OK, - "{ \"pan\": \"1234\", \"fido_creation_options\": " - "{\"relying_party_id\": \"google.com\"}}"); + ReturnResponse( + net::HTTP_OK, + "{ \"pan\": \"1234\", \"dcvv\": \"321\", \"fido_creation_options\": " + "{\"relying_party_id\": \"google.com\"}}"); EXPECT_EQ(AutofillClient::SUCCESS, result_); EXPECT_EQ("1234", unmask_response_details_->real_pan); + EXPECT_EQ("321", unmask_response_details_->dcvv); EXPECT_EQ("google.com", *unmask_response_details_->fido_creation_options->FindStringKey( "relying_party_id")); @@ -514,51 +518,51 @@ TEST_F(PaymentsClientTest, UnmaskLogsCvcLengthForPaymentRequest) { "Autofill.CardUnmask.CvcLength.ForPaymentRequest", 5, 1); } -TEST_F(PaymentsClientTest, UnmaskLogsBlankCvcLength) { - base::HistogramTester histogram_tester; - StartUnmasking(CardUnmaskOptions() - .with_reason(AutofillClient::UNMASK_FOR_AUTOFILL) - .with_cvc("")); - IssueOAuthToken(); - - histogram_tester.ExpectBucketCount( - "Autofill.CardUnmask.CvcLength.ForAutofill", 0, 1); -} - TEST_F(PaymentsClientTest, OptInSuccess) { - StartOptChangeRequest(/*opt_in=*/true); + StartOptChangeRequest( + PaymentsClient::OptChangeRequestDetails::ENABLE_FIDO_AUTH); IssueOAuthToken(); - ReturnResponse(net::HTTP_OK, "{ \"user_is_opted_in\": true }"); + ReturnResponse(net::HTTP_OK, + "{ \"fido_authentication_info\": { \"user_status\": " + "\"FIDO_AUTH_ENABLED\"}}"); EXPECT_EQ(AutofillClient::SUCCESS, result_); - EXPECT_TRUE(user_is_opted_in_.value()); + EXPECT_TRUE(opt_change_response_.user_is_opted_in.value()); } TEST_F(PaymentsClientTest, OptInServerUnresponsive) { - StartOptChangeRequest(/*opt_in=*/true); + StartOptChangeRequest( + PaymentsClient::OptChangeRequestDetails::ENABLE_FIDO_AUTH); IssueOAuthToken(); ReturnResponse(net::HTTP_REQUEST_TIMEOUT, ""); EXPECT_EQ(AutofillClient::NETWORK_ERROR, result_); - EXPECT_FALSE(user_is_opted_in_.value()); + EXPECT_FALSE(opt_change_response_.user_is_opted_in.has_value()); } TEST_F(PaymentsClientTest, OptOutSuccess) { - StartOptChangeRequest(/*opt_in=*/false); + StartOptChangeRequest( + PaymentsClient::OptChangeRequestDetails::DISABLE_FIDO_AUTH); IssueOAuthToken(); - ReturnResponse(net::HTTP_OK, "{ \"user_is_opted_in\": false }"); + ReturnResponse(net::HTTP_OK, + "{ \"fido_authentication_info\": { \"user_status\": " + "\"FIDO_AUTH_DISABLED\"}}"); EXPECT_EQ(AutofillClient::SUCCESS, result_); - EXPECT_FALSE(user_is_opted_in_.value()); + EXPECT_FALSE(opt_change_response_.user_is_opted_in.value()); } TEST_F(PaymentsClientTest, EnrollAttemptReturnsCreationOptions) { - StartOptChangeRequest(/*opt_in=*/true); + StartOptChangeRequest( + PaymentsClient::OptChangeRequestDetails::ENABLE_FIDO_AUTH); IssueOAuthToken(); ReturnResponse(net::HTTP_OK, - "{ \"user_is_opted_in\": false, \"fido_creation_options\": { " - "\"relying_party_id\": \"google.com\"} }"); + "{ \"fido_authentication_info\": { \"user_status\": " + "\"FIDO_AUTH_DISABLED\"," + "\"fido_creation_options\": {" + "\"relying_party_id\": \"google.com\"}}}"); EXPECT_EQ(AutofillClient::SUCCESS, result_); - EXPECT_FALSE(user_is_opted_in_.value()); + EXPECT_FALSE(opt_change_response_.user_is_opted_in.value()); EXPECT_EQ("google.com", - *fido_creation_options_.FindStringKey("relying_party_id")); + *opt_change_response_.fido_creation_options->FindStringKey( + "relying_party_id")); } TEST_F(PaymentsClientTest, GetDetailsSuccess) { @@ -806,7 +810,7 @@ TEST_F(PaymentsClientTest, GetUploadAccountFromSyncTest) { // Issue a token for the secondary account. identity_test_env_.WaitForAccessTokenRequestIfNecessaryAndRespondWithToken( secondary_account_info.account_id, "secondary_account_token", - base::Time::Now() + base::TimeDelta::FromDays(10)); + AutofillClock::Now() + base::TimeDelta::FromDays(10)); // Verify the auth header. std::string auth_header_value; diff --git a/chromium/components/autofill/core/browser/payments/test_credit_card_fido_authenticator.cc b/chromium/components/autofill/core/browser/payments/test_credit_card_fido_authenticator.cc index e8a556c8081..bce45685589 100644 --- a/chromium/components/autofill/core/browser/payments/test_credit_card_fido_authenticator.cc +++ b/chromium/components/autofill/core/browser/payments/test_credit_card_fido_authenticator.cc @@ -22,12 +22,14 @@ TestCreditCardFIDOAuthenticator::~TestCreditCardFIDOAuthenticator() {} void TestCreditCardFIDOAuthenticator::GetAssertion( PublicKeyCredentialRequestOptionsPtr request_options) { - request_options_ = std::move(request_options); + request_options_ = request_options->Clone(); + CreditCardFIDOAuthenticator::GetAssertion(std::move(request_options)); } void TestCreditCardFIDOAuthenticator::MakeCredential( PublicKeyCredentialCreationOptionsPtr creation_options) { - creation_options_ = std::move(creation_options); + creation_options_ = creation_options->Clone(); + CreditCardFIDOAuthenticator::MakeCredential(std::move(creation_options)); } // static diff --git a/chromium/components/autofill/core/browser/payments/test_payments_client.cc b/chromium/components/autofill/core/browser/payments/test_payments_client.cc index 88ba1252ce0..144de6532c2 100644 --- a/chromium/components/autofill/core/browser/payments/test_payments_client.cc +++ b/chromium/components/autofill/core/browser/payments/test_payments_client.cc @@ -7,7 +7,9 @@ #include <memory> #include <unordered_map> +#include "base/json/json_reader.h" #include "base/strings/utf_string_conversions.h" +#include "base/values.h" #include "components/autofill/core/browser/personal_data_manager.h" #include "services/network/public/cpp/shared_url_loader_factory.h" @@ -33,7 +35,8 @@ TestPaymentsClient::~TestPaymentsClient() {} void TestPaymentsClient::GetUnmaskDetails(GetUnmaskDetailsCallback callback, const std::string& app_locale) { - std::move(callback).Run(AutofillClient::SUCCESS, unmask_details_); + if (should_return_unmask_details_) + std::move(callback).Run(AutofillClient::SUCCESS, unmask_details_); } void TestPaymentsClient::GetUploadDetails( @@ -56,7 +59,7 @@ void TestPaymentsClient::GetUploadDetails( app_locale == "en-US" ? AutofillClient::SUCCESS : AutofillClient::PERMANENT_FAILURE, base::ASCIIToUTF16("this is a context token"), - std::unique_ptr<base::Value>(nullptr), supported_card_bin_ranges_); + TestPaymentsClient::LegalMessage(), supported_card_bin_ranges_); } void TestPaymentsClient::UploadCard( @@ -76,13 +79,20 @@ void TestPaymentsClient::MigrateCards( "this is display text"); } +void TestPaymentsClient::ShouldReturnUnmaskDetailsImmediately( + bool should_return_unmask_details) { + should_return_unmask_details_ = should_return_unmask_details; +} + void TestPaymentsClient::AllowFidoRegistration(bool offer_fido_opt_in) { + should_return_unmask_details_ = true; unmask_details_.offer_fido_opt_in = offer_fido_opt_in; } void TestPaymentsClient::AddFidoEligibleCard(std::string server_id, std::string credential_id, std::string relying_party_id) { + should_return_unmask_details_ = true; unmask_details_.offer_fido_opt_in = false; unmask_details_.unmask_auth_method = AutofillClient::UnmaskAuthMethod::FIDO; unmask_details_.fido_eligible_card_ids.insert(server_id); @@ -136,5 +146,40 @@ void TestPaymentsClient::SetSupportedBINRanges( supported_card_bin_ranges_ = bin_ranges; } +void TestPaymentsClient::SetUseInvalidLegalMessageInGetUploadDetails( + bool use_invalid_legal_message) { + use_invalid_legal_message_ = use_invalid_legal_message; +} + +std::unique_ptr<base::Value> TestPaymentsClient::LegalMessage() { + if (use_invalid_legal_message_) { + // Legal message is invalid because it's missing the url. + return std::unique_ptr<base::Value>( + base::JSONReader::ReadDeprecated("{" + " \"line\" : [ {" + " \"template\": \"Panda {0}.\"," + " \"template_parameter\": [ {" + " \"display_text\": \"bear\"" + " } ]" + " } ]" + "}")); + return std::unique_ptr<base::Value>(base::JSONReader::ReadDeprecated("{}")); + } else { + return std::unique_ptr<base::Value>(base::JSONReader::ReadDeprecated( + "{" + " \"line\" : [ {" + " \"template\": \"The legal documents are: {0} and {1}.\"," + " \"template_parameter\" : [ {" + " \"display_text\" : \"Terms of Service\"," + " \"url\": \"http://www.example.com/tos\"" + " }, {" + " \"display_text\" : \"Privacy Policy\"," + " \"url\": \"http://www.example.com/pp\"" + " } ]" + " } ]" + "}")); + } +} + } // namespace payments } // namespace autofill diff --git a/chromium/components/autofill/core/browser/payments/test_payments_client.h b/chromium/components/autofill/core/browser/payments/test_payments_client.h index 01763edd62f..03ffcc0bea3 100644 --- a/chromium/components/autofill/core/browser/payments/test_payments_client.h +++ b/chromium/components/autofill/core/browser/payments/test_payments_client.h @@ -56,6 +56,10 @@ class TestPaymentsClient : public payments::PaymentsClient { const std::vector<MigratableCreditCard>& migratable_credit_cards, MigrateCardsCallback callback) override; + // Some metrics are affected by the latency of GetUnmaskDetails, so it is + // useful to control whether or not GetUnmaskDetails() is responded to. + void ShouldReturnUnmaskDetailsImmediately(bool should_return_unmask_details); + void AllowFidoRegistration(bool offer_fido_opt_in = true); void AddFidoEligibleCard(std::string server_id, @@ -70,6 +74,9 @@ class TestPaymentsClient : public payments::PaymentsClient { void SetSupportedBINRanges(std::vector<std::pair<int, int>> bin_ranges); + void SetUseInvalidLegalMessageInGetUploadDetails( + bool use_invalid_legal_message); + int detected_values_in_upload_details() const { return detected_values_; } const std::vector<AutofillProfile>& addresses_in_upload_details() const { return upload_details_addresses_; @@ -89,6 +96,9 @@ class TestPaymentsClient : public payments::PaymentsClient { private: std::string server_id_; + // Some metrics are affected by the latency of GetUnmaskDetails, so it is + // useful to control whether or not GetUnmaskDetails() is responded to. + bool should_return_unmask_details_ = true; AutofillClient::UnmaskDetails unmask_details_; std::vector<std::pair<int, int>> supported_card_bin_ranges_; std::vector<AutofillProfile> upload_details_addresses_; @@ -99,6 +109,8 @@ class TestPaymentsClient : public payments::PaymentsClient { int billable_service_number_; PaymentsClient::UploadCardSource upload_card_source_; std::unique_ptr<std::unordered_map<std::string, std::string>> save_result_; + bool use_invalid_legal_message_ = false; + std::unique_ptr<base::Value> LegalMessage(); DISALLOW_COPY_AND_ASSIGN(TestPaymentsClient); }; diff --git a/chromium/components/autofill/core/browser/payments/test_strike_database.cc b/chromium/components/autofill/core/browser/payments/test_strike_database.cc index 7b96773e25b..d80df7cbac1 100644 --- a/chromium/components/autofill/core/browser/payments/test_strike_database.cc +++ b/chromium/components/autofill/core/browser/payments/test_strike_database.cc @@ -5,6 +5,7 @@ #include "components/autofill/core/browser/payments/test_strike_database.h" #include "components/autofill/core/browser/proto/strike_data.pb.h" +#include "components/autofill/core/common/autofill_clock.h" namespace autofill { @@ -36,7 +37,7 @@ void TestStrikeDatabase::AddEntryWithNumStrikes(const std::string& key, StrikeData strike_data; strike_data.set_num_strikes(num_strikes); strike_data.set_last_update_timestamp( - base::Time::Now().ToDeltaSinceWindowsEpoch().InMicroseconds()); + AutofillClock::Now().ToDeltaSinceWindowsEpoch().InMicroseconds()); db_[key] = strike_data; } diff --git a/chromium/components/autofill/core/browser/payments/upi_vpa_save_manager.cc b/chromium/components/autofill/core/browser/payments/upi_vpa_save_manager.cc new file mode 100644 index 00000000000..324fbfc53bc --- /dev/null +++ b/chromium/components/autofill/core/browser/payments/upi_vpa_save_manager.cc @@ -0,0 +1,21 @@ +// Copyright 2019 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 "components/autofill/core/browser/payments/upi_vpa_save_manager.h" + +#include "base/logging.h" + +namespace autofill { + +UpiVpaSaveManager::UpiVpaSaveManager(PersonalDataManager* personal_data_manager) + : personal_data_manager_(personal_data_manager) {} + +void UpiVpaSaveManager::OfferLocalSave(const std::string& upi_id) { + // TODO(crbug.com/986289): Ask user with a prompt before saving. + + if (personal_data_manager_) + personal_data_manager_->AddVPA(upi_id); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/payments/upi_vpa_save_manager.h b/chromium/components/autofill/core/browser/payments/upi_vpa_save_manager.h new file mode 100644 index 00000000000..b97a6330969 --- /dev/null +++ b/chromium/components/autofill/core/browser/payments/upi_vpa_save_manager.h @@ -0,0 +1,29 @@ +// Copyright 2019 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. + +#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_UPI_VPA_SAVE_MANAGER_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_UPI_VPA_SAVE_MANAGER_H_ + +#include "base/strings/string16.h" +#include "components/autofill/core/browser/personal_data_manager.h" + +namespace autofill { + +class UpiVpaSaveManager { + public: + UpiVpaSaveManager(PersonalDataManager* personal_data_manager); + ~UpiVpaSaveManager() = default; + + void OfferLocalSave(const std::string& upi_id); + + private: + // The personal data manager, used to save and load personal data to/from the + // web database. This is overridden by the AutofillManagerTest. + // Weak reference. May be nullptr, which indicates OTR. + PersonalDataManager* personal_data_manager_; +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_UPI_VPA_SAVE_MANAGER_H_ diff --git a/chromium/components/autofill/core/browser/personal_data_manager.cc b/chromium/components/autofill/core/browser/personal_data_manager.cc index 41cd4d5bb74..10787c72754 100644 --- a/chromium/components/autofill/core/browser/personal_data_manager.cc +++ b/chromium/components/autofill/core/browser/personal_data_manager.cc @@ -47,6 +47,7 @@ #include "components/autofill/core/common/autofill_clock.h" #include "components/autofill/core/common/autofill_constants.h" #include "components/autofill/core/common/autofill_features.h" +#include "components/autofill/core/common/autofill_payments_features.h" #include "components/autofill/core/common/autofill_prefs.h" #include "components/autofill/core/common/autofill_switches.h" #include "components/autofill/core/common/autofill_util.h" @@ -641,6 +642,13 @@ void PersonalDataManager::RecordUseOf(const AutofillDataModel& data_model) { } } +void PersonalDataManager::AddVPA(const std::string& vpa_id) { + DCHECK(!vpa_id.empty()); + if (is_off_the_record_ || !database_helper_->GetLocalDatabase()) + return; + database_helper_->GetLocalDatabase()->AddVPA(vpa_id); +} + void PersonalDataManager::AddProfile(const AutofillProfile& profile) { if (!IsAutofillProfileEnabled()) return; @@ -1305,7 +1313,7 @@ bool PersonalDataManager::ShouldSuggestServerCards() const { // seeing them in the dropdown. if (!prefs::IsUserOptedInWalletSyncTransport( pref_service_, - sync_service_->GetAuthenticatedAccountInfo().account_id)) { + sync_service_->GetAuthenticatedAccountInfo().account_id.id)) { return false; } } @@ -1349,7 +1357,6 @@ void PersonalDataManager::ClearProfileNonSettingsOrigins() { UpdateProfileInDB(*profile, /*enforced=*/true); } } - } void PersonalDataManager::ClearCreditCardNonSettingsOrigins() { @@ -1369,35 +1376,6 @@ void PersonalDataManager::ClearCreditCardNonSettingsOrigins() { Refresh(); } -void PersonalDataManager::MoveJapanCityToStreetAddress() { - if (!database_helper_->GetLocalDatabase()) - return; - - // Don't run if the migration has already been performed. - if (pref_service_->GetBoolean(prefs::kAutofillJapanCityFieldMigrated)) - return; - - base::string16 japan_country_code = base::ASCIIToUTF16("JP"); - base::string16 line_separator = base::ASCIIToUTF16("\n"); - for (AutofillProfile* profile : GetProfiles()) { - base::string16 country_code = profile->GetRawInfo(ADDRESS_HOME_COUNTRY); - base::string16 city = profile->GetRawInfo(ADDRESS_HOME_CITY); - if (country_code == japan_country_code && !city.empty()) { - base::string16 street_address = - profile->GetRawInfo(ADDRESS_HOME_STREET_ADDRESS); - street_address = street_address.empty() - ? city - : street_address + line_separator + city; - profile->SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, street_address); - profile->SetRawInfo(ADDRESS_HOME_CITY, base::string16()); - UpdateProfileInDB(*profile, /*enforced=*/true); - } - } - - // Set the pref so that this migration is never run again. - pref_service_->SetBoolean(prefs::kAutofillJapanCityFieldMigrated, true); -} - void PersonalDataManager::OnValidated(const AutofillProfile* profile) { if (!profile) return; @@ -1452,94 +1430,6 @@ const ProfileValidityMap& PersonalDataManager::GetProfileValidityByGUID( return empty_validity_map; } -// static -std::string PersonalDataManager::MergeProfile( - const AutofillProfile& new_profile, - std::vector<std::unique_ptr<AutofillProfile>>* existing_profiles, - const std::string& app_locale, - std::vector<AutofillProfile>* merged_profiles) { - merged_profiles->clear(); - - // Sort the existing profiles in decreasing order of frecency, so the "best" - // profiles are checked first. Put the verified profiles last so the non - // verified profiles get deduped among themselves before reaching the verified - // profiles. - // TODO(crbug.com/620521): Remove the check for verified from the sort. - base::Time comparison_time = AutofillClock::Now(); - std::sort(existing_profiles->begin(), existing_profiles->end(), - [comparison_time](const std::unique_ptr<AutofillProfile>& a, - const std::unique_ptr<AutofillProfile>& b) { - if (a->IsVerified() != b->IsVerified()) - return !a->IsVerified(); - return a->HasGreaterFrecencyThan(b.get(), comparison_time); - }); - - // Set to true if |existing_profiles| already contains an equivalent profile. - bool matching_profile_found = false; - std::string guid = new_profile.guid(); - - // If we have already saved this address, merge in any missing values. - // Only merge with the first match. Merging the new profile into the existing - // one preserves the validity of credit card's billing address reference. - AutofillProfileComparator comparator(app_locale); - for (const auto& existing_profile : *existing_profiles) { - if (!matching_profile_found && - comparator.AreMergeable(new_profile, *existing_profile) && - existing_profile->SaveAdditionalInfo(new_profile, app_locale)) { - // Unverified profiles should always be updated with the newer data, - // whereas verified profiles should only ever be overwritten by verified - // data. If an automatically aggregated profile would overwrite a - // verified profile, just drop it. - matching_profile_found = true; - guid = existing_profile->guid(); - - // We set the modification date so that immediate requests for profiles - // will properly reflect the fact that this profile has been modified - // recently. After writing to the database and refreshing the local copies - // the profile will have a very slightly newer time reflecting what's - // actually stored in the database. - existing_profile->set_modification_date(AutofillClock::Now()); - } - merged_profiles->push_back(*existing_profile); - } - - // If the new profile was not merged with an existing one, add it to the list. - if (!matching_profile_found) { - merged_profiles->push_back(new_profile); - // Similar to updating merged profiles above, set the modification date on - // new profiles. - merged_profiles->back().set_modification_date(AutofillClock::Now()); - AutofillMetrics::LogProfileActionOnFormSubmitted( - AutofillMetrics::NEW_PROFILE_CREATED); - } - - return guid; -} - -bool PersonalDataManager::IsCountryOfInterest( - const std::string& country_code) const { - DCHECK_EQ(2U, country_code.size()); - - const std::vector<AutofillProfile*>& profiles = GetProfiles(); - std::list<std::string> country_codes; - for (size_t i = 0; i < profiles.size(); ++i) { - country_codes.push_back(base::ToLowerASCII( - base::UTF16ToASCII(profiles[i]->GetRawInfo(ADDRESS_HOME_COUNTRY)))); - } - - std::string timezone_country = CountryCodeForCurrentTimezone(); - if (!timezone_country.empty()) - country_codes.push_back(base::ToLowerASCII(timezone_country)); - - // Only take the locale into consideration if all else fails. - if (country_codes.empty()) { - country_codes.push_back(base::ToLowerASCII( - AutofillCountry::CountryCodeForLocale(app_locale()))); - } - - return base::Contains(country_codes, base::ToLowerASCII(country_code)); -} - const std::string& PersonalDataManager::GetDefaultCountryCodeForNewAddress() const { if (default_country_code_.empty()) @@ -1762,8 +1652,8 @@ std::string PersonalDataManager::SaveImportedProfile( return std::string(); std::vector<AutofillProfile> profiles; - std::string guid = - MergeProfile(imported_profile, &web_profiles_, app_locale_, &profiles); + std::string guid = AutofillProfileComparator::MergeProfile( + imported_profile, &web_profiles_, app_locale_, &profiles); SetProfiles(&profiles); return guid; } @@ -1799,6 +1689,10 @@ std::string PersonalDataManager::SaveImportedCreditCard( credit_cards.push_back(imported_card); SetCreditCards(&credit_cards); + + // After a card is saved locally, notifies the observers. + OnCreditCardSaved(/*is_local_card=*/true); + return guid; } @@ -1945,7 +1839,8 @@ bool PersonalDataManager::ShouldShowCardsFromAccountOption() const { features::kAutofillEnableAccountWalletStorage)); bool is_opted_in = prefs::IsUserOptedInWalletSyncTransport( - pref_service_, sync_service_->GetAuthenticatedAccountInfo().account_id); + pref_service_, + sync_service_->GetAuthenticatedAccountInfo().account_id.id); AutofillMetrics::LogWalletSyncTransportCardsOptIn(is_opted_in); @@ -1957,7 +1852,7 @@ void PersonalDataManager::OnUserAcceptedCardsFromAccountOption() { DCHECK_EQ(AutofillSyncSigninState::kSignedInAndWalletSyncTransportEnabled, GetSyncSigninState()); prefs::SetUserOptedInWalletSyncTransport( - pref_service_, sync_service_->GetAuthenticatedAccountInfo().account_id, + pref_service_, sync_service_->GetAuthenticatedAccountInfo().account_id.id, /*opted_in=*/true); } @@ -2014,7 +1909,8 @@ void PersonalDataManager::OnUserAcceptedUpstreamOffer() { if (GetSyncSigninState() == AutofillSyncSigninState::kSignedInAndWalletSyncTransportEnabled) { prefs::SetUserOptedInWalletSyncTransport( - pref_service_, sync_service_->GetAuthenticatedAccountInfo().account_id, + pref_service_, + sync_service_->GetAuthenticatedAccountInfo().account_id.id, /*opted_in=*/true); } } @@ -2033,6 +1929,16 @@ void PersonalDataManager::NotifyPersonalDataObserver() { } } +void PersonalDataManager::OnCreditCardSaved(bool is_local_card) { + if (!base::FeatureList::IsEnabled( + features::kAutofillCreditCardUploadFeedback)) { + return; + } + for (PersonalDataManagerObserver& observer : observers_) + observer.OnCreditCardSaved( + /*should_show_sign_in_promo_if_applicable=*/is_local_card); +} + std::vector<Suggestion> PersonalDataManager::GetSuggestionsForCards( const AutofillType& type, const base::string16& field_contents, @@ -2421,9 +2327,6 @@ void PersonalDataManager::ApplyAddressFixesAndCleanups() { // Ran everytime it is called. ClearProfileNonSettingsOrigins(); - - // One-time fix, otherwise NOP. - MoveJapanCityToStreetAddress(); } void PersonalDataManager::ApplyCardFixesAndCleanups() { diff --git a/chromium/components/autofill/core/browser/personal_data_manager.h b/chromium/components/autofill/core/browser/personal_data_manager.h index 912b4542d40..0e652553e43 100644 --- a/chromium/components/autofill/core/browser/personal_data_manager.h +++ b/chromium/components/autofill/core/browser/personal_data_manager.h @@ -154,6 +154,10 @@ class PersonalDataManager : public KeyedService, std::string OnAcceptedLocalCreditCardSave( const CreditCard& imported_credit_card); + // Triggered when the user accepts saving a VPA value. Stores the |vpa_id| to + // the database. + virtual void AddVPA(const std::string& vpa_id); + // Adds |profile| to the web database. virtual void AddProfile(const AutofillProfile& profile); @@ -264,12 +268,6 @@ class PersonalDataManager : public KeyedService, // Returns the profiles to suggest to the user, ordered by frecency. std::vector<AutofillProfile*> GetProfilesToSuggest() const; - // Remove profiles that whose |type| field is flagged as invalid, if Chrome - // is configured to not make suggestions based on invalid data. - static void MaybeRemoveInvalidSuggestions( - const AutofillType& type, - std::vector<AutofillProfile*>* profiles); - // Returns Suggestions corresponding to the focused field's |type| and // |field_contents|, i.e. what the user has typed. |field_is_autofilled| is // true if the field has already been autofilled, and |field_types| stores the @@ -325,22 +323,6 @@ class PersonalDataManager : public KeyedService, const std::string& app_locale() const { return app_locale_; } - // Merges |new_profile| into one of the |existing_profiles| if possible; - // otherwise appends |new_profile| to the end of that list. Fills - // |merged_profiles| with the result. Returns the |guid| of the new or updated - // profile. - static std::string MergeProfile( - const AutofillProfile& new_profile, - std::vector<std::unique_ptr<AutofillProfile>>* existing_profiles, - const std::string& app_locale, - std::vector<AutofillProfile>* merged_profiles); - - // Returns true if |country_code| is a country that the user is likely to - // be associated with the user. More concretely, it checks if there are any - // addresses with this country or if the user's system timezone is in the - // given country. - virtual bool IsCountryOfInterest(const std::string& country_code) const; - // Returns our best guess for the country a user is likely to use when // inputting a new address. The value is calculated once and cached, so it // will only update when Chrome is restarted. @@ -387,6 +369,10 @@ class PersonalDataManager : public KeyedService, // Notifies observers that the waiting should be stopped. void NotifyPersonalDataObserver(); + // Called when at least one (can be multiple) card was saved. |is_local_card| + // indicates if the card is saved to local storage. + void OnCreditCardSaved(bool is_local_card); + void set_client_profile_validator_for_test( AutofillProfileValidator* validator) { client_profile_validator_ = validator; @@ -466,8 +452,6 @@ class PersonalDataManager : public KeyedService, FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest, ClearCreditCardNonSettingsOrigins); FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest, - MoveJapanCityToStreetAddress); - FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest, RequestProfileServerValidity); FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest, GetProfileSuggestions_Validity); @@ -555,12 +539,6 @@ class PersonalDataManager : public KeyedService, void ClearProfileNonSettingsOrigins(); void ClearCreditCardNonSettingsOrigins(); - // Appends the value of the city field of a JP address to its street address - // field, separated by a newline, and clears the city field. - // TODO(rouslan): Remove this migration in or after October 2019. See bug: - // https://crbug.com/871301 - void MoveJapanCityToStreetAddress(); - // Called when the |profile| is validated by the AutofillProfileValidator, // updates the profiles on the |ongoing_profile_changes_| and the DB. virtual void OnValidated(const AutofillProfile* profile); @@ -703,13 +681,6 @@ class PersonalDataManager : public KeyedService, void RemoveAutofillProfileByGUIDAndBlankCreditCardReference( const std::string& guid); - // Returns true if an address can be deleted in a major version upgrade. - // An address is deletable if it is unverified, and not used by a valid - // credit card as billing address, and not used for a long time(13 months). - bool IsAddressDeletable( - AutofillProfile* profile, - const std::unordered_set<std::string>& used_billing_address_guids); - // Applies various fixes and cleanups on autofill addresses. void ApplyAddressFixesAndCleanups(); diff --git a/chromium/components/autofill/core/browser/personal_data_manager_observer.h b/chromium/components/autofill/core/browser/personal_data_manager_observer.h index d09bc934f9e..4a40dbba366 100644 --- a/chromium/components/autofill/core/browser/personal_data_manager_observer.h +++ b/chromium/components/autofill/core/browser/personal_data_manager_observer.h @@ -13,7 +13,7 @@ namespace autofill { class PersonalDataManagerObserver { public: // Notifies the observer that the PersonalDataManager changed in some way. - virtual void OnPersonalDataChanged() = 0; + virtual void OnPersonalDataChanged() {} // Called when there is insufficient data to fill a form. Used for testing. virtual void OnInsufficientFormData() {} @@ -22,6 +22,11 @@ class PersonalDataManagerObserver { // handle. virtual void OnPersonalDataFinishedProfileTasks() {} + // Notifies the observer whenever at least one (can be multiple) credit card + // is suceesfully saved. + virtual void OnCreditCardSaved(bool should_show_sign_in_promo_if_applicable) { + } + protected: virtual ~PersonalDataManagerObserver() {} }; diff --git a/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc b/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc index ed12c5048f9..16972154375 100644 --- a/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc @@ -286,19 +286,8 @@ class PersonalDataManagerTestBase { return account_info; } - void MoveJapanCityToStreetAddress(PersonalDataManager* personal_data, - int move_times) { - base::RunLoop run_loop; - EXPECT_CALL(personal_data_observer_, OnPersonalDataFinishedProfileTasks()) - .WillRepeatedly(QuitMessageLoop(&run_loop)); - EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()) - .Times(move_times); - personal_data->MoveJapanCityToStreetAddress(); - run_loop.Run(); - } - - base::test::TaskEnvironment task_environment_{ - base::test::TaskEnvironment::MainThreadType::UI}; + base::test::SingleThreadTaskEnvironment task_environment_{ + base::test::SingleThreadTaskEnvironment::MainThreadType::UI}; std::unique_ptr<PrefService> prefs_; network::TestURLLoaderFactory test_url_loader_factory_; signin::IdentityTestEnvironment identity_test_env_; @@ -958,7 +947,7 @@ TEST_F(PersonalDataManagerTest, SaveImportedProfileSetModificationDate) { SaveImportedProfileToPersonalDataManager(profile); const std::vector<AutofillProfile*>& profiles = personal_data_->GetProfiles(); ASSERT_EQ(1U, profiles.size()); - EXPECT_GT(base::TimeDelta::FromMilliseconds(1000), + EXPECT_GT(base::TimeDelta::FromMilliseconds(2000), AutofillClock::Now() - profiles[0]->modification_date()); } @@ -1338,7 +1327,6 @@ TEST_F(PersonalDataManagerTest, AddFullCardAsMaskedCard) { "378282246310005" /* American Express */, "04", "2999", "1"); - personal_data_->AddFullServerCreditCard(server_card); WaitForOnPersonalDataChanged(); @@ -2773,7 +2761,7 @@ TEST_F(PersonalDataManagerTest, profile2.set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(10)); profile2.set_use_count(1); - EXPECT_TRUE(profile1.HasGreaterFrecencyThan(&profile2, base::Time::Now())); + EXPECT_TRUE(profile1.HasGreaterFrecencyThan(&profile2, AutofillClock::Now())); AddProfileToPersonalDataManager(profile1); AddProfileToPersonalDataManager(profile2); @@ -4478,7 +4466,7 @@ TEST_F(PersonalDataManagerTest, MergeProfile_Frecency) { // Merge the imported profile into the existing profiles. std::vector<AutofillProfile> profiles; - std::string guid = personal_data_->MergeProfile( + std::string guid = AutofillProfileComparator::MergeProfile( imported_profile, &existing_profiles, "US-EN", &profiles); // The new profile should be merged into the "fox" profile. @@ -4527,7 +4515,7 @@ TEST_F(PersonalDataManagerTest, MAYBE_MergeProfile_UsageStats) { // Merge the imported profile into the existing profiles. std::vector<AutofillProfile> profiles; - std::string guid = personal_data_->MergeProfile( + std::string guid = AutofillProfileComparator::MergeProfile( imported_profile, &existing_profiles, "US-EN", &profiles); // The new profile should be merged into the existing profile. @@ -6881,108 +6869,6 @@ TEST_F(PersonalDataManagerTest, ClearCreditCardNonSettingsOrigins) { personal_data_->GetCreditCardsToSuggest(false)[3]->origin()); } -// Tests that all city fields in a Japan profile are moved to the street address -// field. -TEST_F(PersonalDataManagerTest, MoveJapanCityToStreetAddress) { - // Turn on sync feature to avoid calling MoveJapanCityToStreetAddress on - // adding the profiles implicitly. - ASSERT_TRUE(TurnOnSyncFeature()); - // A US profile with both street address and a city. - std::string guid0 = base::GenerateGUID(); - { - AutofillProfile profile0(guid0, test::kEmptyOrigin); - test::SetProfileInfo(&profile0, "Homer", "J", "Simpson", - "homer.simpson@abc.com", "", "742. Evergreen Terrace", - "", "Springfield", "IL", "91601", "US", ""); - AddProfileToPersonalDataManager(profile0); - } - - // A JP profile with both street address and a city. - std::string guid1 = base::GenerateGUID(); - { - AutofillProfile profile1(guid1, test::kEmptyOrigin); - test::SetProfileInfo(&profile1, "Homer", "J", "Simpson", - "homer.simpson@abc.com", "", "742. Evergreen Terrace", - "", "Springfield", "IL", "91601", "JP", ""); - AddProfileToPersonalDataManager(profile1); - } - - // A JP profile with only a city. - std::string guid2 = base::GenerateGUID(); - { - AutofillProfile profile2(guid2, test::kEmptyOrigin); - test::SetProfileInfo(&profile2, "Homer", "J", "Simpson", - "homer.simpson@abc.com", "", "", "", "Springfield", - "IL", "91601", "JP", ""); - AddProfileToPersonalDataManager(profile2); - } - - // A JP profile with only a street address. - std::string guid3 = base::GenerateGUID(); - { - AutofillProfile profile3(guid3, test::kEmptyOrigin); - test::SetProfileInfo(&profile3, "Homer", "J", "Simpson", - "homer.simpson@abc.com", "", "742. Evergreen Terrace", - "", "", "IL", "91601", "JP", ""); - AddProfileToPersonalDataManager(profile3); - } - - // A JP profile with neither a street address nor a city. - std::string guid4 = base::GenerateGUID(); - { - AutofillProfile profile4(guid4, test::kEmptyOrigin); - test::SetProfileInfo(&profile4, "Homer", "J", "Simpson", - "homer.simpson@abc.com", "", "", "", "", "IL", "91601", - "JP", ""); - AddProfileToPersonalDataManager(profile4); - } - auto profiles = personal_data_->GetProfiles(); - ASSERT_EQ(5U, profiles.size()); - - MoveJapanCityToStreetAddress( - personal_data_.get(), - 2); // For the japan profiles where the city is not empty. - - { - AutofillProfile* profile0 = personal_data_->GetProfileByGUID(guid0); - EXPECT_EQ(base::ASCIIToUTF16("742. Evergreen Terrace"), - profile0->GetRawInfo(ADDRESS_HOME_STREET_ADDRESS)); - EXPECT_EQ(base::ASCIIToUTF16("Springfield"), - profile0->GetRawInfo(ADDRESS_HOME_CITY)); - } - - { - AutofillProfile* profile1 = personal_data_->GetProfileByGUID(guid1); - EXPECT_EQ(base::ASCIIToUTF16("742. Evergreen Terrace\nSpringfield"), - profile1->GetRawInfo(ADDRESS_HOME_STREET_ADDRESS)); - EXPECT_EQ(base::ASCIIToUTF16("742. Evergreen Terrace"), - profile1->GetRawInfo(ADDRESS_HOME_LINE1)); - EXPECT_EQ(base::ASCIIToUTF16("Springfield"), - profile1->GetRawInfo(ADDRESS_HOME_LINE2)); - EXPECT_TRUE(profile1->GetRawInfo(ADDRESS_HOME_CITY).empty()); - } - - { - AutofillProfile* profile2 = personal_data_->GetProfileByGUID(guid2); - EXPECT_EQ(base::ASCIIToUTF16("Springfield"), - profile2->GetRawInfo(ADDRESS_HOME_STREET_ADDRESS)); - EXPECT_TRUE(profile2->GetRawInfo(ADDRESS_HOME_CITY).empty()); - } - - { - AutofillProfile* profile3 = personal_data_->GetProfileByGUID(guid3); - EXPECT_EQ(base::ASCIIToUTF16("742. Evergreen Terrace"), - profile3->GetRawInfo(ADDRESS_HOME_STREET_ADDRESS)); - EXPECT_TRUE(profile3->GetRawInfo(ADDRESS_HOME_CITY).empty()); - } - - { - AutofillProfile* profile4 = personal_data_->GetProfileByGUID(guid4); - EXPECT_TRUE(profile4->GetRawInfo(ADDRESS_HOME_STREET_ADDRESS).empty()); - EXPECT_TRUE(profile4->GetRawInfo(ADDRESS_HOME_CITY).empty()); - } -} - // Tests that all the non settings origins of autofill profiles are cleared even // if sync is disabled. TEST_F( @@ -7192,7 +7078,7 @@ TEST_F(PersonalDataManagerTest, RequestProfileServerValidity) { AutofillDataModel::INVALID, AutofillDataModel::VALID, AutofillDataModel::UNVALIDATED, AutofillDataModel::INVALID}; ASSERT_EQ(types.size(), states.size()); - for (unsigned long i = 0; i < types.size(); ++i) { + for (uint64_t i = 0; i < types.size(); ++i) { (*profile_validity_map .mutable_field_validity_states())[static_cast<int>(types[i])] = static_cast<int>(states[i]); @@ -7231,7 +7117,7 @@ TEST_F(PersonalDataManagerTest, RequestProfileServerValidity) { auto validities = personal_data_->GetProfileValidityByGUID(guid).field_validity_states(); ASSERT_EQ(validities.size(), types.size()); - for (unsigned long i = 0; i < types.size(); ++i) + for (uint64_t i = 0; i < types.size(); ++i) EXPECT_EQ(validities.at(types[i]), states[i]); guid = "00000000-0000-0000-0000-0000000000002"; @@ -7952,7 +7838,7 @@ namespace { class OneTimeObserver : public PersonalDataManagerObserver { public: - OneTimeObserver(PersonalDataManager* manager) : manager_(manager) {} + explicit OneTimeObserver(PersonalDataManager* manager) : manager_(manager) {} ~OneTimeObserver() override { if (manager_) diff --git a/chromium/components/autofill/core/browser/proto/server.proto b/chromium/components/autofill/core/browser/proto/server.proto index 67002a10096..72e0e4f60bc 100644 --- a/chromium/components/autofill/core/browser/proto/server.proto +++ b/chromium/components/autofill/core/browser/proto/server.proto @@ -337,11 +337,11 @@ message AutofillUploadContents { // Whether the password has any lowercase letter. optional bool password_has_lowercase_letter = 25; - // Whether the password has any uppercase letter. - optional bool password_has_uppercase_letter = 26; + // Deprecated since M80: Whether the password has any uppercase letter. + optional bool password_has_uppercase_letter = 26 [deprecated = true]; - // Whether the password has any digit. - optional bool password_has_numeric = 27; + // Deprecated since M80: Whether the password has any digit. + optional bool password_has_numeric = 27 [deprecated = true]; // Whether the password has any special symbol. optional bool password_has_special_symbol = 28; diff --git a/chromium/components/autofill/core/browser/test_autofill_client.cc b/chromium/components/autofill/core/browser/test_autofill_client.cc index 89baf299c69..a04561e1fe1 100644 --- a/chromium/components/autofill/core/browser/test_autofill_client.cc +++ b/chromium/components/autofill/core/browser/test_autofill_client.cc @@ -88,7 +88,7 @@ void TestAutofillClient::ShowLocalCardMigrationDialog( } void TestAutofillClient::ConfirmMigrateLocalCardToCloud( - std::unique_ptr<base::DictionaryValue> legal_message, + const LegalMessageLines& legal_message_lines, const std::string& user_email, const std::vector<MigratableCreditCard>& migratable_credit_cards, LocalCardMigrationCallback start_migrating_cards_callback) { @@ -107,9 +107,20 @@ void TestAutofillClient::ShowLocalCardMigrationResults( const std::vector<MigratableCreditCard>& migratable_credit_cards, MigrationDeleteCardCallback delete_local_card_callback) {} +#if !defined(OS_ANDROID) && !defined(OS_IOS) +void TestAutofillClient::ShowVerifyPendingDialog( + base::OnceClosure cancel_card_verification_callback) {} + +void TestAutofillClient::CloseVerifyPendingDialog() {} +#endif + void TestAutofillClient::ShowWebauthnOfferDialog( WebauthnOfferDialogCallback callback) {} +bool TestAutofillClient::CloseWebauthnOfferDialog() { + return true; +} + void TestAutofillClient::ConfirmSaveAutofillProfile( const AutofillProfile& profile, base::OnceClosure callback) { @@ -128,13 +139,15 @@ void TestAutofillClient::ConfirmSaveCreditCardLocally( std::move(callback).Run(AutofillClient::ACCEPTED); } -#if defined(OS_ANDROID) +#if defined(OS_ANDROID) || defined(OS_IOS) void TestAutofillClient::ConfirmAccountNameFixFlow( base::OnceCallback<void(const base::string16&)> callback) { credit_card_name_fix_flow_bubble_was_shown_ = true; std::move(callback).Run(base::string16(base::ASCIIToUTF16("Gaia Name"))); } +#endif // defined(OS_ANDROID) || defined(OS_IOS) +#if defined(OS_ANDROID) void TestAutofillClient::ConfirmExpirationDateFixFlow( const CreditCard& card, base::OnceCallback<void(const base::string16&, const base::string16&)> @@ -148,7 +161,7 @@ void TestAutofillClient::ConfirmExpirationDateFixFlow( void TestAutofillClient::ConfirmSaveCreditCardToCloud( const CreditCard& card, - std::unique_ptr<base::DictionaryValue> legal_message, + const LegalMessageLines& legal_message_lines, SaveCreditCardOptions options, UploadSaveCardPromptCallback callback) { offer_to_save_credit_card_bubble_was_shown_ = options.show_prompt; diff --git a/chromium/components/autofill/core/browser/test_autofill_client.h b/chromium/components/autofill/core/browser/test_autofill_client.h index 2f8b9bdda12..92b7019d260 100644 --- a/chromium/components/autofill/core/browser/test_autofill_client.h +++ b/chromium/components/autofill/core/browser/test_autofill_client.h @@ -16,6 +16,7 @@ #include "build/build_config.h" #include "components/autofill/core/browser/autofill_client.h" #include "components/autofill/core/browser/mock_autocomplete_history_manager.h" +#include "components/autofill/core/browser/payments/legal_message_line.h" #include "components/autofill/core/browser/payments/test_payments_client.h" #include "components/autofill/core/browser/payments/test_strike_database.h" #include "components/autofill/core/browser/test_address_normalizer.h" @@ -55,7 +56,7 @@ class TestAutofillClient : public AutofillClient { void ShowLocalCardMigrationDialog( base::OnceClosure show_migration_dialog_closure) override; void ConfirmMigrateLocalCardToCloud( - std::unique_ptr<base::DictionaryValue> legal_message, + const LegalMessageLines& legal_message_lines, const std::string& user_email, const std::vector<MigratableCreditCard>& migratable_credit_cards, LocalCardMigrationCallback start_migrating_cards_callback) override; @@ -64,16 +65,25 @@ class TestAutofillClient : public AutofillClient { const base::string16& tip_message, const std::vector<MigratableCreditCard>& migratable_credit_cards, MigrationDeleteCardCallback delete_local_card_callback) override; +#if !defined(OS_ANDROID) && !defined(OS_IOS) + void ShowVerifyPendingDialog( + base::OnceClosure cancel_card_verification_callback) override; + void CloseVerifyPendingDialog() override; +#endif void ShowWebauthnOfferDialog(WebauthnOfferDialogCallback callback) override; + bool CloseWebauthnOfferDialog() override; void ConfirmSaveAutofillProfile(const AutofillProfile& profile, base::OnceClosure callback) override; void ConfirmSaveCreditCardLocally( const CreditCard& card, SaveCreditCardOptions options, LocalSaveCardPromptCallback callback) override; -#if defined(OS_ANDROID) +#if defined(OS_ANDROID) || defined(OS_IOS) void ConfirmAccountNameFixFlow( base::OnceCallback<void(const base::string16&)> callback) override; +#endif // defined(OS_ANDROID) || defined(OS_IOS) + +#if defined(OS_ANDROID) void ConfirmExpirationDateFixFlow( const CreditCard& card, base::OnceCallback<void(const base::string16&, const base::string16&)> @@ -81,7 +91,7 @@ class TestAutofillClient : public AutofillClient { #endif // defined(OS_ANDROID) void ConfirmSaveCreditCardToCloud( const CreditCard& card, - std::unique_ptr<base::DictionaryValue> legal_message, + const LegalMessageLines& legal_message_lines, SaveCreditCardOptions options, UploadSaveCardPromptCallback callback) override; void CreditCardUploadCompleted(bool card_saved) override; diff --git a/chromium/components/autofill/core/browser/test_autofill_clock.h b/chromium/components/autofill/core/browser/test_autofill_clock.h index 2b973619433..19ea0a8962b 100644 --- a/chromium/components/autofill/core/browser/test_autofill_clock.h +++ b/chromium/components/autofill/core/browser/test_autofill_clock.h @@ -19,7 +19,7 @@ namespace autofill { // Handles the customization of the time in tests. Replaces the clock in // AutofillClock with a test version that can be manipulated from this class. // Automatically resets a normal clock to AutofillClock when this gets -// destroyed, +// destroyed. class TestAutofillClock { public: TestAutofillClock(); diff --git a/chromium/components/autofill/core/browser/test_autofill_driver.cc b/chromium/components/autofill/core/browser/test_autofill_driver.cc index dd51b9637b6..e7ce762307b 100644 --- a/chromium/components/autofill/core/browser/test_autofill_driver.cc +++ b/chromium/components/autofill/core/browser/test_autofill_driver.cc @@ -79,7 +79,7 @@ void TestAutofillDriver::RendererShouldPreviewFieldWithValue( } void TestAutofillDriver::RendererShouldSetSuggestionAvailability( - bool available) {} + const mojom::AutofillState state) {} void TestAutofillDriver::PopupHidden() { } diff --git a/chromium/components/autofill/core/browser/test_autofill_driver.h b/chromium/components/autofill/core/browser/test_autofill_driver.h index d65c2b23cd8..f524de90b92 100644 --- a/chromium/components/autofill/core/browser/test_autofill_driver.h +++ b/chromium/components/autofill/core/browser/test_autofill_driver.h @@ -46,7 +46,8 @@ class TestAutofillDriver : public AutofillDriver { void RendererShouldFillFieldWithValue(const base::string16& value) override; void RendererShouldPreviewFieldWithValue( const base::string16& value) override; - void RendererShouldSetSuggestionAvailability(bool available) override; + void RendererShouldSetSuggestionAvailability( + const mojom::AutofillState state) override; void PopupHidden() override; gfx::RectF TransformBoundingBoxToViewportCoordinates( const gfx::RectF& bounding_box) override; diff --git a/chromium/components/autofill/core/browser/test_autofill_external_delegate.cc b/chromium/components/autofill/core/browser/test_autofill_external_delegate.cc index f38acc764ca..d97cfccf5c2 100644 --- a/chromium/components/autofill/core/browser/test_autofill_external_delegate.cc +++ b/chromium/components/autofill/core/browser/test_autofill_external_delegate.cc @@ -68,8 +68,11 @@ bool TestAutofillExternalDelegate::HasActiveScreenReader() const { } void TestAutofillExternalDelegate::OnAutofillAvailabilityEvent( - bool has_suggestions) { - has_suggestions_available_on_field_focus_ = has_suggestions; + const mojom::AutofillState state) { + if (state == mojom::AutofillState::kAutofillAvailable) + has_suggestions_available_on_field_focus_ = true; + else if (state == mojom::AutofillState::kNoSuggestions) + has_suggestions_available_on_field_focus_ = false; } void TestAutofillExternalDelegate::WaitForPopupHidden() { diff --git a/chromium/components/autofill/core/browser/test_autofill_external_delegate.h b/chromium/components/autofill/core/browser/test_autofill_external_delegate.h index 7757ba9682e..2aa50399df6 100644 --- a/chromium/components/autofill/core/browser/test_autofill_external_delegate.h +++ b/chromium/components/autofill/core/browser/test_autofill_external_delegate.h @@ -31,7 +31,7 @@ class TestAutofillExternalDelegate : public AutofillExternalDelegate { bool autoselect_first_suggestion, bool is_all_server_suggestions) override; bool HasActiveScreenReader() const override; - void OnAutofillAvailabilityEvent(bool has_suggestions) override; + void OnAutofillAvailabilityEvent(const mojom::AutofillState state) override; // Functions unique to TestAutofillExternalDelegate. diff --git a/chromium/components/autofill/core/browser/test_autofill_tick_clock.cc b/chromium/components/autofill/core/browser/test_autofill_tick_clock.cc new file mode 100644 index 00000000000..c558ed8b8f6 --- /dev/null +++ b/chromium/components/autofill/core/browser/test_autofill_tick_clock.cc @@ -0,0 +1,31 @@ +// Copyright 2019 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 "components/autofill/core/browser/test_autofill_tick_clock.h" + +#include <utility> + +#include "base/test/simple_test_tick_clock.h" +#include "components/autofill/core/common/autofill_tick_clock.h" + +namespace autofill { + +TestAutofillTickClock::TestAutofillTickClock() { + AutofillTickClock::SetTestTickClock(&test_tick_clock_); +} + +TestAutofillTickClock::~TestAutofillTickClock() { + // Destroys the test tick_clock and resets a normal tick_clock. + AutofillTickClock::SetTickClock(); +} + +void TestAutofillTickClock::SetNowTicks(base::TimeTicks now) { + test_tick_clock_.SetNowTicks(now); +} + +void TestAutofillTickClock::Advance(base::TimeDelta delta) { + test_tick_clock_.Advance(delta); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/test_autofill_tick_clock.h b/chromium/components/autofill/core/browser/test_autofill_tick_clock.h new file mode 100644 index 00000000000..481d493725c --- /dev/null +++ b/chromium/components/autofill/core/browser/test_autofill_tick_clock.h @@ -0,0 +1,42 @@ +// Copyright 2019 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. + +#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_TEST_AUTOFILL_TICK_CLOCK_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_TEST_AUTOFILL_TICK_CLOCK_H_ + +#include <memory> + +#include "base/macros.h" +#include "base/test/simple_test_tick_clock.h" + +namespace base { +class TimeTicks; +} // namespace base + +namespace autofill { + +// Handles the customization of the time in tests. Replaces the tick_clock in +// AutofillTickClock with a test version that can be manipulated from this +// class. Automatically resets a normal tick_clock to AutofillTickClock when +// this gets destroyed. +class TestAutofillTickClock { + public: + TestAutofillTickClock(); + ~TestAutofillTickClock(); + + // Set the time to be returned from AutofillTickClock::Now() calls. + void SetNowTicks(base::TimeTicks now); + + // Advances the tick_clock by |delta|. + void Advance(base::TimeDelta delta); + + private: + base::SimpleTestTickClock test_tick_clock_; + + DISALLOW_COPY_AND_ASSIGN(TestAutofillTickClock); +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_TEST_AUTOFILL_TICK_CLOCK_H_ diff --git a/chromium/components/autofill/core/browser/test_personal_data_manager.cc b/chromium/components/autofill/core/browser/test_personal_data_manager.cc index f1d48c3d9bc..02cdc5f4ebb 100644 --- a/chromium/components/autofill/core/browser/test_personal_data_manager.cc +++ b/chromium/components/autofill/core/browser/test_personal_data_manager.cc @@ -46,6 +46,10 @@ std::string TestPersonalDataManager::SaveImportedCreditCard( return imported_credit_card.guid(); } +void TestPersonalDataManager::AddVPA(const std::string& profile) { + num_times_save_vpa_called_++; +} + void TestPersonalDataManager::AddProfile(const AutofillProfile& profile) { std::unique_ptr<AutofillProfile> profile_ptr = std::make_unique<AutofillProfile>(profile); @@ -222,8 +226,7 @@ bool TestPersonalDataManager::ShouldSuggestServerCards() const { return IsAutofillCreditCardEnabled() && IsAutofillWalletImportEnabled(); } -std::string TestPersonalDataManager::CountryCodeForCurrentTimezone() - const { +std::string TestPersonalDataManager::CountryCodeForCurrentTimezone() const { return timezone_country_code_; } diff --git a/chromium/components/autofill/core/browser/test_personal_data_manager.h b/chromium/components/autofill/core/browser/test_personal_data_manager.h index 688fe7fc9b8..a4d9dde9e4e 100644 --- a/chromium/components/autofill/core/browser/test_personal_data_manager.h +++ b/chromium/components/autofill/core/browser/test_personal_data_manager.h @@ -34,6 +34,7 @@ class TestPersonalDataManager : public PersonalDataManager { const AutofillProfile& imported_profile) override; std::string SaveImportedCreditCard( const CreditCard& imported_credit_card) override; + void AddVPA(const std::string& vpa) override; void AddProfile(const AutofillProfile& profile) override; void UpdateProfile(const AutofillProfile& profile) override; void RemoveByGUID(const std::string& guid) override; @@ -91,6 +92,8 @@ class TestPersonalDataManager : public PersonalDataManager { return num_times_save_imported_credit_card_called_; } + int num_times_save_vpa_called() const { return num_times_save_vpa_called_; } + bool sync_service_initialized() const { return sync_service_initialized_; } void SetAutofillEnabled(bool autofill_enabled) { @@ -129,6 +132,7 @@ class TestPersonalDataManager : public PersonalDataManager { std::string default_country_code_; int num_times_save_imported_profile_called_ = 0; int num_times_save_imported_credit_card_called_ = 0; + int num_times_save_vpa_called_ = 0; base::Optional<bool> autofill_enabled_; base::Optional<bool> autofill_profile_enabled_; base::Optional<bool> autofill_credit_card_enabled_; diff --git a/chromium/components/autofill/core/browser/ui/payments/card_name_fix_flow_controller.h b/chromium/components/autofill/core/browser/ui/payments/card_name_fix_flow_controller.h new file mode 100644 index 00000000000..96d9faeb47f --- /dev/null +++ b/chromium/components/autofill/core/browser/ui/payments/card_name_fix_flow_controller.h @@ -0,0 +1,36 @@ +// Copyright 2019 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. + +#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_UI_PAYMENTS_CARD_NAME_FIX_FLOW_CONTROLLER_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_UI_PAYMENTS_CARD_NAME_FIX_FLOW_CONTROLLER_H_ + +#include "base/strings/string16.h" + +namespace autofill { + +// Enables the user to accept or deny cardholder name fix flow prompt. +// Only used on mobile. +class CardNameFixFlowController { + public: + virtual ~CardNameFixFlowController() {} + + // Interaction. + virtual void OnConfirmNameDialogClosed() = 0; + virtual void OnNameAccepted(const base::string16& name) = 0; + virtual void OnDismissed() = 0; + + // State. + virtual int GetIconId() const = 0; + virtual base::string16 GetCancelButtonLabel() const = 0; + virtual base::string16 GetInferredCardholderName() const = 0; + virtual base::string16 GetInferredNameTooltipText() const = 0; + virtual base::string16 GetInputLabel() const = 0; + virtual base::string16 GetInputPlaceholderText() const = 0; + virtual base::string16 GetSaveButtonLabel() const = 0; + virtual base::string16 GetTitleText() const = 0; +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_UI_PAYMENTS_CARD_NAME_FIX_FLOW_CONTROLLER_H_ diff --git a/chromium/components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl.cc b/chromium/components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl.cc new file mode 100644 index 00000000000..ea355c2fbff --- /dev/null +++ b/chromium/components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl.cc @@ -0,0 +1,127 @@ +// Copyright 2019 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 "components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl.h" + +#include <utility> + +#include "base/logging.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "base/values.h" +#include "build/branding_buildflags.h" +#include "build/build_config.h" +#include "components/autofill/core/browser/autofill_metrics.h" +#include "components/autofill/core/browser/ui/payments/card_name_fix_flow_view.h" +#include "components/grit/components_scaled_resources.h" +#include "components/strings/grit/components_strings.h" +#include "ui/base/l10n/l10n_util.h" + +namespace autofill { + +CardNameFixFlowControllerImpl::CardNameFixFlowControllerImpl() {} + +CardNameFixFlowControllerImpl::~CardNameFixFlowControllerImpl() { + if (card_name_fix_flow_view_) + card_name_fix_flow_view_->ControllerGone(); + + if (shown_ && !had_user_interaction_) { + AutofillMetrics::LogCardholderNameFixFlowPromptEvent( + AutofillMetrics:: + CARDHOLDER_NAME_FIX_FLOW_PROMPT_CLOSED_WITHOUT_INTERACTION); + } +} + +void CardNameFixFlowControllerImpl::Show( + CardNameFixFlowView* card_name_fix_flow_view, + const base::string16& inferred_cardholder_name, + base::OnceCallback<void(const base::string16&)> name_accepted_callback) { + DCHECK(!name_accepted_callback.is_null()); + DCHECK(card_name_fix_flow_view); + + if (card_name_fix_flow_view_) + card_name_fix_flow_view_->ControllerGone(); + card_name_fix_flow_view_ = card_name_fix_flow_view; + + name_accepted_callback_ = std::move(name_accepted_callback); + + inferred_cardholder_name_ = inferred_cardholder_name; + AutofillMetrics::LogSaveCardCardholderNamePrefilled( + !inferred_cardholder_name_.empty()); + + card_name_fix_flow_view_->Show(); + AutofillMetrics::LogCardholderNameFixFlowPromptEvent( + AutofillMetrics::CARDHOLDER_NAME_FIX_FLOW_PROMPT_SHOWN); + shown_ = true; +} + +void CardNameFixFlowControllerImpl::OnConfirmNameDialogClosed() { + card_name_fix_flow_view_ = nullptr; +} + +void CardNameFixFlowControllerImpl::OnNameAccepted(const base::string16& name) { + AutofillMetrics::LogCardholderNameFixFlowPromptEvent( + AutofillMetrics::CARDHOLDER_NAME_FIX_FLOW_PROMPT_ACCEPTED); + had_user_interaction_ = true; + AutofillMetrics::LogSaveCardCardholderNameWasEdited( + inferred_cardholder_name_ != name); + base::string16 trimmed_name; + base::TrimWhitespace(name, base::TRIM_ALL, &trimmed_name); + std::move(name_accepted_callback_).Run(trimmed_name); +} + +void CardNameFixFlowControllerImpl::OnDismissed() { + AutofillMetrics::LogCardholderNameFixFlowPromptEvent( + AutofillMetrics::CARDHOLDER_NAME_FIX_FLOW_PROMPT_DISMISSED); + had_user_interaction_ = true; +} + +int CardNameFixFlowControllerImpl::GetIconId() const { +#if BUILDFLAG(GOOGLE_CHROME_BRANDING) + return IDR_AUTOFILL_GOOGLE_PAY_WITH_DIVIDER; +#else + return 0; +#endif +} + +base::string16 CardNameFixFlowControllerImpl::GetCancelButtonLabel() const { + return l10n_util::GetStringUTF16(IDS_CANCEL); +} + +base::string16 CardNameFixFlowControllerImpl::GetInferredCardholderName() + const { + return inferred_cardholder_name_; +} + +base::string16 CardNameFixFlowControllerImpl::GetInferredNameTooltipText() + const { + return l10n_util::GetStringUTF16( + IDS_AUTOFILL_SAVE_CARD_PROMPT_CARDHOLDER_NAME_TOOLTIP); +} + +base::string16 CardNameFixFlowControllerImpl::GetInputLabel() const { + return l10n_util::GetStringUTF16( + IDS_AUTOFILL_SAVE_CARD_PROMPT_CARDHOLDER_NAME); +} + +base::string16 CardNameFixFlowControllerImpl::GetInputPlaceholderText() const { + return l10n_util::GetStringUTF16( + IDS_AUTOFILL_SAVE_CARD_PROMPT_CARDHOLDER_NAME); +} + +base::string16 CardNameFixFlowControllerImpl::GetSaveButtonLabel() const { +#if defined(OS_IOS) + return l10n_util::GetStringUTF16(IDS_SAVE); +#else + return l10n_util::GetStringUTF16( + IDS_AUTOFILL_FIX_FLOW_PROMPT_SAVE_CARD_LABEL); +#endif +} + +base::string16 CardNameFixFlowControllerImpl::GetTitleText() const { + return l10n_util::GetStringUTF16( + IDS_AUTOFILL_SAVE_CARD_CARDHOLDER_NAME_FIX_FLOW_HEADER); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl.h b/chromium/components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl.h new file mode 100644 index 00000000000..e9f4d9ba72a --- /dev/null +++ b/chromium/components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl.h @@ -0,0 +1,62 @@ +// Copyright 2019 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. + +#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_UI_PAYMENTS_CARD_NAME_FIX_FLOW_CONTROLLER_IMPL_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_UI_PAYMENTS_CARD_NAME_FIX_FLOW_CONTROLLER_IMPL_H_ + +#include <string> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/strings/string16.h" +#include "components/autofill/core/browser/ui/payments/card_name_fix_flow_controller.h" + +namespace autofill { + +class CardNameFixFlowView; + +class CardNameFixFlowControllerImpl : public CardNameFixFlowController { + public: + CardNameFixFlowControllerImpl(); + ~CardNameFixFlowControllerImpl() override; + + void Show(CardNameFixFlowView* card_name_fix_flow_view, + const base::string16& inferred_cardholder_name, + base::OnceCallback<void(const base::string16&)> name_callback); + + // CardNameFixFlowController implementation. + void OnConfirmNameDialogClosed() override; + void OnNameAccepted(const base::string16& name) override; + void OnDismissed() override; + int GetIconId() const override; + base::string16 GetCancelButtonLabel() const override; + base::string16 GetInferredCardholderName() const override; + base::string16 GetInferredNameTooltipText() const override; + base::string16 GetInputLabel() const override; + base::string16 GetInputPlaceholderText() const override; + base::string16 GetSaveButtonLabel() const override; + base::string16 GetTitleText() const override; + + private: + // Inferred cardholder name from Gaia account. + base::string16 inferred_cardholder_name_; + + // View that displays the fix flow prompt. + CardNameFixFlowView* card_name_fix_flow_view_ = nullptr; + + // The callback to call once user confirms their name through the fix flow. + base::OnceCallback<void(const base::string16&)> name_accepted_callback_; + + // Whether the prompt was shown to the user. + bool shown_ = false; + + // Whether the user explicitly accepted or dismissed this prompt. + bool had_user_interaction_ = false; + + DISALLOW_COPY_AND_ASSIGN(CardNameFixFlowControllerImpl); +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_UI_PAYMENTS_CARD_NAME_FIX_FLOW_CONTROLLER_IMPL_H_ diff --git a/chromium/components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl_unittest.cc b/chromium/components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl_unittest.cc new file mode 100644 index 00000000000..c6dc583e35b --- /dev/null +++ b/chromium/components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl_unittest.cc @@ -0,0 +1,147 @@ +// Copyright 2019 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 "components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl.h" + +#include <stddef.h> +#include <memory> + +#include "base/bind.h" +#include "base/macros.h" +#include "base/strings/utf_string_conversions.h" +#include "base/test/metrics/histogram_tester.h" +#include "components/autofill/core/browser/autofill_metrics.h" +#include "components/autofill/core/browser/autofill_test_utils.h" +#include "components/autofill/core/browser/ui/payments/card_name_fix_flow_view.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace autofill { + +class TestCardNameFixFlowView : public CardNameFixFlowView { + public: + void Show() override {} + void ControllerGone() override {} +}; + +class CardNameFixFlowControllerImplGenericTest { + public: + CardNameFixFlowControllerImplGenericTest() {} + + void ShowPromptWithInferredName() { + inferred_name_ = base::ASCIIToUTF16("John Doe"); + ShowPrompt(); + } + + void ShowPromptWithoutInferredName() { + inferred_name_ = base::ASCIIToUTF16(""); + ShowPrompt(); + } + + void AcceptWithInferredName() { controller_->OnNameAccepted(inferred_name_); } + + void AcceptWithEditedName() { + controller_->OnNameAccepted(base::ASCIIToUTF16("Edited Name")); + } + + protected: + std::unique_ptr<TestCardNameFixFlowView> test_card_name_fix_flow_view_; + std::unique_ptr<CardNameFixFlowControllerImpl> controller_; + base::string16 inferred_name_; + base::string16 accepted_name_; + base::WeakPtrFactory<CardNameFixFlowControllerImplGenericTest> + weak_ptr_factory_{this}; + + private: + void OnNameAccepted(const base::string16& name) { accepted_name_ = name; } + + void ShowPrompt() { + controller_->Show( + test_card_name_fix_flow_view_.get(), inferred_name_, + base::BindOnce( + &CardNameFixFlowControllerImplGenericTest::OnNameAccepted, + weak_ptr_factory_.GetWeakPtr())); + } + + DISALLOW_COPY_AND_ASSIGN(CardNameFixFlowControllerImplGenericTest); +}; + +class CardNameFixFlowControllerImplTest + : public CardNameFixFlowControllerImplGenericTest, + public testing::Test { + public: + CardNameFixFlowControllerImplTest() {} + ~CardNameFixFlowControllerImplTest() override {} + + void SetUp() override { + test_card_name_fix_flow_view_.reset(new TestCardNameFixFlowView()); + controller_.reset(new CardNameFixFlowControllerImpl()); + } + + private: + DISALLOW_COPY_AND_ASSIGN(CardNameFixFlowControllerImplTest); +}; + +TEST_F(CardNameFixFlowControllerImplTest, LogShown) { + base::HistogramTester histogram_tester; + ShowPromptWithInferredName(); + + histogram_tester.ExpectUniqueSample( + "Autofill.CardholderNameFixFlowPrompt.Events", + AutofillMetrics::CARDHOLDER_NAME_FIX_FLOW_PROMPT_SHOWN, 1); +} + +TEST_F(CardNameFixFlowControllerImplTest, LogPrefilled) { + base::HistogramTester histogram_tester; + ShowPromptWithInferredName(); + + histogram_tester.ExpectBucketCount("Autofill.SaveCardCardholderNamePrefilled", + true, 1); +} + +TEST_F(CardNameFixFlowControllerImplTest, LogNotPrefilled) { + base::HistogramTester histogram_tester; + ShowPromptWithoutInferredName(); + + histogram_tester.ExpectBucketCount("Autofill.SaveCardCardholderNamePrefilled", + false, 1); +} + +TEST_F(CardNameFixFlowControllerImplTest, LogAccepted) { + base::HistogramTester histogram_tester; + ShowPromptWithInferredName(); + AcceptWithInferredName(); + + histogram_tester.ExpectBucketCount( + "Autofill.CardholderNameFixFlowPrompt.Events", + AutofillMetrics::CARDHOLDER_NAME_FIX_FLOW_PROMPT_ACCEPTED, 1); +} + +TEST_F(CardNameFixFlowControllerImplTest, LogUserAcceptedInferredName) { + base::HistogramTester histogram_tester; + ShowPromptWithInferredName(); + AcceptWithInferredName(); + + histogram_tester.ExpectBucketCount("Autofill.SaveCardCardholderNameWasEdited", + false, 1); +} + +TEST_F(CardNameFixFlowControllerImplTest, LogUserAcceptedEditedName) { + base::HistogramTester histogram_tester; + ShowPromptWithInferredName(); + AcceptWithEditedName(); + + histogram_tester.ExpectBucketCount("Autofill.SaveCardCardholderNameWasEdited", + true, 1); +} + +TEST_F(CardNameFixFlowControllerImplTest, LogDismissed) { + base::HistogramTester histogram_tester; + controller_->OnDismissed(); + + histogram_tester.ExpectBucketCount( + "Autofill.CardholderNameFixFlowPrompt.Events", + AutofillMetrics::CARDHOLDER_NAME_FIX_FLOW_PROMPT_DISMISSED, 1); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/ui/payments/card_name_fix_flow_view.h b/chromium/components/autofill/core/browser/ui/payments/card_name_fix_flow_view.h new file mode 100644 index 00000000000..f9bf6cea366 --- /dev/null +++ b/chromium/components/autofill/core/browser/ui/payments/card_name_fix_flow_view.h @@ -0,0 +1,26 @@ +// Copyright 2019 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. + +#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_UI_PAYMENTS_CARD_NAME_FIX_FLOW_VIEW_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_UI_PAYMENTS_CARD_NAME_FIX_FLOW_VIEW_H_ + +#include "base/macros.h" +#include "base/strings/string16.h" + +namespace autofill { + +// The cross-platform UI interface which prompts the user to confirm their name. +// This object is responsible for its own lifetime. +class CardNameFixFlowView { + public: + virtual void Show() = 0; + virtual void ControllerGone() = 0; + + protected: + virtual ~CardNameFixFlowView() = default; +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_UI_PAYMENTS_CARD_NAME_FIX_FLOW_VIEW_H_ diff --git a/chromium/components/autofill/core/browser/ui/payments/card_name_fix_flow_view_delegate_mobile.cc b/chromium/components/autofill/core/browser/ui/payments/card_name_fix_flow_view_delegate_mobile.cc deleted file mode 100644 index 94d6dc8b052..00000000000 --- a/chromium/components/autofill/core/browser/ui/payments/card_name_fix_flow_view_delegate_mobile.cc +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2018 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 "components/autofill/core/browser/ui/payments/card_name_fix_flow_view_delegate_mobile.h" - -#include <utility> - -#include "base/logging.h" -#include "base/strings/utf_string_conversions.h" -#include "base/values.h" -#include "build/branding_buildflags.h" -#include "components/grit/components_scaled_resources.h" -#include "components/strings/grit/components_strings.h" -#include "ui/base/l10n/l10n_util.h" - -namespace autofill { - -CardNameFixFlowViewDelegateMobile::CardNameFixFlowViewDelegateMobile( - const base::string16& inferred_cardholder_name, - base::OnceCallback<void(const base::string16&)> upload_save_card_callback) - : inferred_cardholder_name_(inferred_cardholder_name), - upload_save_card_callback_(std::move(upload_save_card_callback)), - shown_(false), - had_user_interaction_(false) { - DCHECK(!upload_save_card_callback_.is_null()); - AutofillMetrics::LogSaveCardCardholderNamePrefilled( - !inferred_cardholder_name_.empty()); -} - -CardNameFixFlowViewDelegateMobile::~CardNameFixFlowViewDelegateMobile() { - if (shown_ && !had_user_interaction_) - AutofillMetrics::LogCardholderNameFixFlowPromptEvent( - AutofillMetrics:: - CARDHOLDER_NAME_FIX_FLOW_PROMPT_CLOSED_WITHOUT_INTERACTION); -} - -int CardNameFixFlowViewDelegateMobile::GetIconId() const { -#if BUILDFLAG(GOOGLE_CHROME_BRANDING) - return IDR_AUTOFILL_GOOGLE_PAY_WITH_DIVIDER; -#else - return 0; -#endif -} - -base::string16 CardNameFixFlowViewDelegateMobile::GetTitleText() const { - return l10n_util::GetStringUTF16( - IDS_AUTOFILL_SAVE_CARD_CARDHOLDER_NAME_FIX_FLOW_HEADER); -} - -base::string16 CardNameFixFlowViewDelegateMobile::GetInferredCardHolderName() - const { - return inferred_cardholder_name_; -} - -base::string16 CardNameFixFlowViewDelegateMobile::GetSaveButtonLabel() const { - return l10n_util::GetStringUTF16( - IDS_AUTOFILL_FIX_FLOW_PROMPT_SAVE_CARD_LABEL); -} - -void CardNameFixFlowViewDelegateMobile::Accept(const base::string16& name) { - std::move(upload_save_card_callback_).Run(name); - AutofillMetrics::LogCardholderNameFixFlowPromptEvent( - AutofillMetrics::CARDHOLDER_NAME_FIX_FLOW_PROMPT_ACCEPTED); - had_user_interaction_ = true; - AutofillMetrics::LogSaveCardCardholderNameWasEdited( - inferred_cardholder_name_ != name); -} - -void CardNameFixFlowViewDelegateMobile::Dismissed() { - AutofillMetrics::LogCardholderNameFixFlowPromptEvent( - AutofillMetrics::CARDHOLDER_NAME_FIX_FLOW_PROMPT_DISMISSED); - had_user_interaction_ = true; -} - -void CardNameFixFlowViewDelegateMobile::Shown() { - AutofillMetrics::LogCardholderNameFixFlowPromptEvent( - AutofillMetrics::CARDHOLDER_NAME_FIX_FLOW_PROMPT_SHOWN); - shown_ = true; -} - -} // namespace autofill diff --git a/chromium/components/autofill/core/browser/ui/payments/card_name_fix_flow_view_delegate_mobile.h b/chromium/components/autofill/core/browser/ui/payments/card_name_fix_flow_view_delegate_mobile.h deleted file mode 100644 index 64415a44661..00000000000 --- a/chromium/components/autofill/core/browser/ui/payments/card_name_fix_flow_view_delegate_mobile.h +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2018 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. - -#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_UI_PAYMENTS_CARD_NAME_FIX_FLOW_VIEW_DELEGATE_MOBILE_H_ -#define COMPONENTS_AUTOFILL_CORE_BROWSER_UI_PAYMENTS_CARD_NAME_FIX_FLOW_VIEW_DELEGATE_MOBILE_H_ - -#include <memory> - -#include "base/callback.h" -#include "base/macros.h" -#include "base/strings/string16.h" -#include "components/autofill/core/browser/autofill_metrics.h" - -namespace autofill { - -// Enables the user to accept or deny cardholder name fix flow prompt. -// Only used on mobile. -class CardNameFixFlowViewDelegateMobile { - public: - CardNameFixFlowViewDelegateMobile( - const base::string16& inferred_cardholder_name, - base::OnceCallback<void(const base::string16&)> - upload_save_card_callback); - - ~CardNameFixFlowViewDelegateMobile(); - - int GetIconId() const; - base::string16 GetTitleText() const; - base::string16 GetInferredCardHolderName() const; - base::string16 GetSaveButtonLabel() const; - void Accept(const base::string16& name); - void Dismissed(); - void Shown(); - - private: - // Inferred cardholder name from Gaia account. - base::string16 inferred_cardholder_name_; - - // The callback to save the credit card to Google Payments once user accepts - // fix flow. - base::OnceCallback<void(const base::string16&)> upload_save_card_callback_; - - // Whether the prompt was shown to the user. - bool shown_; - - // Did the user ever explicitly accept or dismiss this prompt? - bool had_user_interaction_; - - DISALLOW_COPY_AND_ASSIGN(CardNameFixFlowViewDelegateMobile); -}; - -} // namespace autofill - -#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_UI_PAYMENTS_CARD_NAME_FIX_FLOW_VIEW_DELEGATE_MOBILE_H_ diff --git a/chromium/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller.h b/chromium/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller.h index 8a33e40f7dd..e2df819d693 100644 --- a/chromium/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller.h +++ b/chromium/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller.h @@ -33,6 +33,7 @@ class CardUnmaskPromptController { virtual bool ShouldRequestExpirationDate() const = 0; virtual bool CanStoreLocally() const = 0; virtual bool GetStoreLocallyStartState() const = 0; + virtual bool GetWebauthnOfferStartState() const = 0; virtual base::TimeDelta GetSuccessMessageDuration() const = 0; virtual AutofillClient::PaymentsRpcResult GetVerificationResult() const = 0; diff --git a/chromium/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.cc b/chromium/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.cc index f4918d2ff28..14a43d4a1a5 100644 --- a/chromium/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.cc +++ b/chromium/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.cc @@ -134,6 +134,16 @@ void CardUnmaskPromptControllerImpl::OnUnmaskPromptAccepted( pending_details_.should_store_pan = false; } + // The FIDO authentication checkbox is only shown when the local storage + // checkbox is not shown and the flag is turned on. If it is shown, then + // remember the last choice the user made on this device. + if (base::FeatureList::IsEnabled( + features::kAutofillCreditCardAuthentication) && + !CanStoreLocally()) { + pref_service_->SetBoolean( + prefs::kAutofillCreditCardFidoAuthOfferCheckboxState, enable_fido_auth); + } + // There is a chance the delegate has disappeared (i.e. tab closed) before the // unmask response came in. Avoid a crash. if (delegate_) @@ -219,6 +229,11 @@ bool CardUnmaskPromptControllerImpl::GetStoreLocallyStartState() const { prefs::kAutofillWalletImportStorageCheckboxState); } +bool CardUnmaskPromptControllerImpl::GetWebauthnOfferStartState() const { + return pref_service_->GetBoolean( + prefs::kAutofillCreditCardFidoAuthOfferCheckboxState); +} + bool CardUnmaskPromptControllerImpl::InputCvcIsValid( const base::string16& input_text) const { base::string16 trimmed_text; diff --git a/chromium/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.h b/chromium/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.h index dd174d24644..345de4c8d82 100644 --- a/chromium/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.h +++ b/chromium/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.h @@ -48,6 +48,7 @@ class CardUnmaskPromptControllerImpl : public CardUnmaskPromptController { bool ShouldRequestExpirationDate() const override; bool CanStoreLocally() const override; bool GetStoreLocallyStartState() const override; + bool GetWebauthnOfferStartState() const override; bool InputCvcIsValid(const base::string16& input_text) const override; bool InputExpirationIsValid(const base::string16& month, const base::string16& year) const override; diff --git a/chromium/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl_unittest.cc b/chromium/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl_unittest.cc index 418ac6c636e..56275f7a2dc 100644 --- a/chromium/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl_unittest.cc +++ b/chromium/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl_unittest.cc @@ -12,6 +12,7 @@ #include "base/macros.h" #include "base/strings/utf_string_conversions.h" #include "base/test/metrics/histogram_tester.h" +#include "base/test/scoped_feature_list.h" #include "components/autofill/core/browser/autofill_client.h" #include "components/autofill/core/browser/autofill_metrics.h" #include "components/autofill/core/browser/autofill_test_utils.h" @@ -105,11 +106,12 @@ class CardUnmaskPromptControllerImplGenericTest { AutofillClient::UNMASK_FOR_AUTOFILL, delegate_->GetWeakPtr()); } - void ShowPromptAndSimulateResponse(bool should_store_pan) { + void ShowPromptAndSimulateResponse(bool should_store_pan, + bool enable_fido_auth) { ShowPrompt(); controller_->OnUnmaskPromptAccepted(ASCIIToUTF16("444"), ASCIIToUTF16("01"), ASCIIToUTF16("2050"), should_store_pan, - /*enable_fido_auth=*/false); + enable_fido_auth); EXPECT_EQ(should_store_pan, pref_service_->GetBoolean( prefs::kAutofillWalletImportStorageCheckboxState)); @@ -121,6 +123,7 @@ class CardUnmaskPromptControllerImplGenericTest { value); } + base::test::ScopedFeatureList scoped_feature_list_; std::unique_ptr<TestCardUnmaskPromptView> test_unmask_prompt_view_; std::unique_ptr<TestingPrefServiceSimple> pref_service_; std::unique_ptr<TestCardUnmaskPromptController> controller_; @@ -144,6 +147,8 @@ class CardUnmaskPromptControllerImplTest delegate_.reset(new TestCardUnmaskDelegate()); pref_service_->registry()->RegisterBooleanPref( prefs::kAutofillWalletImportStorageCheckboxState, false); + pref_service_->registry()->RegisterBooleanPref( + prefs::kAutofillCreditCardFidoAuthOfferCheckboxState, true); } private: @@ -169,7 +174,8 @@ TEST_F(CardUnmaskPromptControllerImplTest, LogClosedNoAttempts) { } TEST_F(CardUnmaskPromptControllerImplTest, LogClosedAbandonUnmasking) { - ShowPromptAndSimulateResponse(/*should_store_pan=*/false); + ShowPromptAndSimulateResponse(/*should_store_pan=*/false, + /*enable_fido_auth=*/false); base::HistogramTester histogram_tester; controller_->OnUnmaskDialogClosed(); @@ -180,7 +186,8 @@ TEST_F(CardUnmaskPromptControllerImplTest, LogClosedAbandonUnmasking) { } TEST_F(CardUnmaskPromptControllerImplTest, LogClosedFailedToUnmaskRetriable) { - ShowPromptAndSimulateResponse(/*should_store_pan=*/false); + ShowPromptAndSimulateResponse(/*should_store_pan=*/false, + /*enable_fido_auth=*/false); controller_->OnVerificationResult(AutofillClient::TRY_AGAIN_FAILURE); base::HistogramTester histogram_tester; @@ -198,7 +205,8 @@ TEST_F(CardUnmaskPromptControllerImplTest, LogClosedFailedToUnmaskRetriable) { TEST_F(CardUnmaskPromptControllerImplTest, LogClosedFailedToUnmaskNonRetriable) { - ShowPromptAndSimulateResponse(/*should_store_pan=*/false); + ShowPromptAndSimulateResponse(/*should_store_pan=*/false, + /*enable_fido_auth=*/false); controller_->OnVerificationResult(AutofillClient::PERMANENT_FAILURE); base::HistogramTester histogram_tester; @@ -216,7 +224,8 @@ TEST_F(CardUnmaskPromptControllerImplTest, } TEST_F(CardUnmaskPromptControllerImplTest, LogUnmaskedCardFirstAttempt) { - ShowPromptAndSimulateResponse(/*should_store_pan=*/false); + ShowPromptAndSimulateResponse(/*should_store_pan=*/false, + /*enable_fido_auth=*/false); base::HistogramTester histogram_tester; controller_->OnVerificationResult(AutofillClient::SUCCESS); @@ -232,7 +241,8 @@ TEST_F(CardUnmaskPromptControllerImplTest, LogUnmaskedCardFirstAttempt) { } TEST_F(CardUnmaskPromptControllerImplTest, LogUnmaskedCardAfterFailure) { - ShowPromptAndSimulateResponse(/*should_store_pan=*/false); + ShowPromptAndSimulateResponse(/*should_store_pan=*/false, + /*enable_fido_auth=*/false); controller_->OnVerificationResult(AutofillClient::TRY_AGAIN_FAILURE); controller_->OnUnmaskPromptAccepted(ASCIIToUTF16("444"), ASCIIToUTF16("01"), ASCIIToUTF16("2050"), @@ -249,7 +259,8 @@ TEST_F(CardUnmaskPromptControllerImplTest, LogUnmaskedCardAfterFailure) { } TEST_F(CardUnmaskPromptControllerImplTest, LogSavedCardLocally) { - ShowPromptAndSimulateResponse(/*should_store_pan=*/true); + ShowPromptAndSimulateResponse(/*should_store_pan=*/true, + /*enable_fido_auth=*/false); base::HistogramTester histogram_tester; controller_->OnVerificationResult(AutofillClient::SUCCESS); @@ -262,7 +273,8 @@ TEST_F(CardUnmaskPromptControllerImplTest, LogSavedCardLocally) { TEST_F(CardUnmaskPromptControllerImplTest, LogDidOptIn) { SetImportCheckboxState(false); - ShowPromptAndSimulateResponse(/*should_store_pan=*/true); + ShowPromptAndSimulateResponse(/*should_store_pan=*/true, + /*enable_fido_auth=*/false); base::HistogramTester histogram_tester; controller_->OnUnmaskDialogClosed(); @@ -273,7 +285,8 @@ TEST_F(CardUnmaskPromptControllerImplTest, LogDidOptIn) { TEST_F(CardUnmaskPromptControllerImplTest, LogDidNotOptIn) { SetImportCheckboxState(false); - ShowPromptAndSimulateResponse(/*should_store_pan=*/false); + ShowPromptAndSimulateResponse(/*should_store_pan=*/false, + /*enable_fido_auth=*/false); base::HistogramTester histogram_tester; controller_->OnUnmaskDialogClosed(); @@ -284,7 +297,8 @@ TEST_F(CardUnmaskPromptControllerImplTest, LogDidNotOptIn) { TEST_F(CardUnmaskPromptControllerImplTest, LogDidOptOut) { SetImportCheckboxState(true); - ShowPromptAndSimulateResponse(/*should_store_pan=*/false); + ShowPromptAndSimulateResponse(/*should_store_pan=*/false, + /*enable_fido_auth=*/false); base::HistogramTester histogram_tester; controller_->OnUnmaskDialogClosed(); @@ -295,7 +309,8 @@ TEST_F(CardUnmaskPromptControllerImplTest, LogDidOptOut) { TEST_F(CardUnmaskPromptControllerImplTest, LogDidNotOptOut) { SetImportCheckboxState(true); - ShowPromptAndSimulateResponse(/*should_store_pan=*/true); + ShowPromptAndSimulateResponse(/*should_store_pan=*/true, + /*enable_fido_auth=*/false); base::HistogramTester histogram_tester; controller_->OnUnmaskDialogClosed(); @@ -306,7 +321,8 @@ TEST_F(CardUnmaskPromptControllerImplTest, LogDidNotOptOut) { TEST_F(CardUnmaskPromptControllerImplTest, DontLogForHiddenCheckbox) { controller_->set_can_store_locally(false); - ShowPromptAndSimulateResponse(/*should_store_pan=*/false); + ShowPromptAndSimulateResponse(/*should_store_pan=*/false, + /*enable_fido_auth=*/false); base::HistogramTester histogram_tester; controller_->OnUnmaskDialogClosed(); @@ -324,6 +340,22 @@ TEST_F(CardUnmaskPromptControllerImplTest, DontLogForHiddenCheckbox) { AutofillMetrics::UNMASK_PROMPT_LOCAL_SAVE_DID_NOT_OPT_OUT, 0); } +TEST_F(CardUnmaskPromptControllerImplTest, + FidoAuthOfferCheckboxStatePersistent) { + scoped_feature_list_.InitAndEnableFeature( + features::kAutofillCreditCardAuthentication); + controller_->set_can_store_locally(false); + ShowPromptAndSimulateResponse(/*should_store_pan=*/false, + /*enable_fido_auth=*/true); + EXPECT_TRUE(pref_service_->GetBoolean( + prefs::kAutofillCreditCardFidoAuthOfferCheckboxState)); + + ShowPromptAndSimulateResponse(/*should_store_pan=*/false, + /*enable_fido_auth=*/false); + EXPECT_FALSE(pref_service_->GetBoolean( + prefs::kAutofillCreditCardFidoAuthOfferCheckboxState)); +} + TEST_F(CardUnmaskPromptControllerImplTest, LogDurationNoAttempts) { ShowPrompt(); base::HistogramTester histogram_tester; @@ -336,7 +368,8 @@ TEST_F(CardUnmaskPromptControllerImplTest, LogDurationNoAttempts) { } TEST_F(CardUnmaskPromptControllerImplTest, LogDurationAbandonUnmasking) { - ShowPromptAndSimulateResponse(/*should_store_pan=*/false); + ShowPromptAndSimulateResponse(/*should_store_pan=*/false, + /*enable_fido_auth=*/false); base::HistogramTester histogram_tester; controller_->OnUnmaskDialogClosed(); @@ -347,7 +380,8 @@ TEST_F(CardUnmaskPromptControllerImplTest, LogDurationAbandonUnmasking) { } TEST_F(CardUnmaskPromptControllerImplTest, LogDurationFailedToUnmaskRetriable) { - ShowPromptAndSimulateResponse(/*should_store_pan=*/false); + ShowPromptAndSimulateResponse(/*should_store_pan=*/false, + /*enable_fido_auth=*/false); controller_->OnVerificationResult(AutofillClient::TRY_AGAIN_FAILURE); base::HistogramTester histogram_tester; @@ -360,7 +394,8 @@ TEST_F(CardUnmaskPromptControllerImplTest, LogDurationFailedToUnmaskRetriable) { TEST_F(CardUnmaskPromptControllerImplTest, LogDurationFailedToUnmaskNonRetriable) { - ShowPromptAndSimulateResponse(/*should_store_pan=*/false); + ShowPromptAndSimulateResponse(/*should_store_pan=*/false, + /*enable_fido_auth=*/false); controller_->OnVerificationResult(AutofillClient::PERMANENT_FAILURE); base::HistogramTester histogram_tester; @@ -372,7 +407,8 @@ TEST_F(CardUnmaskPromptControllerImplTest, } TEST_F(CardUnmaskPromptControllerImplTest, LogDurationCardFirstAttempt) { - ShowPromptAndSimulateResponse(/*should_store_pan=*/false); + ShowPromptAndSimulateResponse(/*should_store_pan=*/false, + /*enable_fido_auth=*/false); base::HistogramTester histogram_tester; controller_->OnVerificationResult(AutofillClient::SUCCESS); @@ -385,7 +421,8 @@ TEST_F(CardUnmaskPromptControllerImplTest, LogDurationCardFirstAttempt) { TEST_F(CardUnmaskPromptControllerImplTest, LogDurationUnmaskedCardAfterFailure) { - ShowPromptAndSimulateResponse(/*should_store_pan=*/false); + ShowPromptAndSimulateResponse(/*should_store_pan=*/false, + /*enable_fido_auth=*/false); controller_->OnVerificationResult(AutofillClient::TRY_AGAIN_FAILURE); controller_->OnUnmaskPromptAccepted( base::ASCIIToUTF16("444"), base::ASCIIToUTF16("01"), @@ -402,7 +439,8 @@ TEST_F(CardUnmaskPromptControllerImplTest, } TEST_F(CardUnmaskPromptControllerImplTest, LogTimeBeforeAbandonUnmasking) { - ShowPromptAndSimulateResponse(/*should_store_pan=*/false); + ShowPromptAndSimulateResponse(/*should_store_pan=*/false, + /*enable_fido_auth=*/false); base::HistogramTester histogram_tester; controller_->OnUnmaskDialogClosed(); @@ -412,7 +450,8 @@ TEST_F(CardUnmaskPromptControllerImplTest, LogTimeBeforeAbandonUnmasking) { } TEST_F(CardUnmaskPromptControllerImplTest, LogRealPanResultSuccess) { - ShowPromptAndSimulateResponse(/*should_store_pan=*/false); + ShowPromptAndSimulateResponse(/*should_store_pan=*/false, + /*enable_fido_auth=*/false); base::HistogramTester histogram_tester; controller_->OnVerificationResult(AutofillClient::SUCCESS); @@ -422,7 +461,8 @@ TEST_F(CardUnmaskPromptControllerImplTest, LogRealPanResultSuccess) { } TEST_F(CardUnmaskPromptControllerImplTest, LogRealPanTryAgainFailure) { - ShowPromptAndSimulateResponse(/*should_store_pan=*/false); + ShowPromptAndSimulateResponse(/*should_store_pan=*/false, + /*enable_fido_auth=*/false); base::HistogramTester histogram_tester; controller_->OnVerificationResult(AutofillClient::TRY_AGAIN_FAILURE); @@ -433,7 +473,8 @@ TEST_F(CardUnmaskPromptControllerImplTest, LogRealPanTryAgainFailure) { } TEST_F(CardUnmaskPromptControllerImplTest, LogUnmaskingDurationResultSuccess) { - ShowPromptAndSimulateResponse(/*should_store_pan=*/false); + ShowPromptAndSimulateResponse(/*should_store_pan=*/false, + /*enable_fido_auth=*/false); base::HistogramTester histogram_tester; controller_->OnVerificationResult(AutofillClient::SUCCESS); @@ -446,7 +487,8 @@ TEST_F(CardUnmaskPromptControllerImplTest, LogUnmaskingDurationResultSuccess) { TEST_F(CardUnmaskPromptControllerImplTest, LogUnmaskingDurationTryAgainFailure) { - ShowPromptAndSimulateResponse(/*should_store_pan=*/false); + ShowPromptAndSimulateResponse(/*should_store_pan=*/false, + /*enable_fido_auth=*/false); base::HistogramTester histogram_tester; controller_->OnVerificationResult(AutofillClient::TRY_AGAIN_FAILURE); diff --git a/chromium/components/autofill/core/browser/ui/popup_item_ids.h b/chromium/components/autofill/core/browser/ui/popup_item_ids.h index 917f68981fb..52193d51e05 100644 --- a/chromium/components/autofill/core/browser/ui/popup_item_ids.h +++ b/chromium/components/autofill/core/browser/ui/popup_item_ids.h @@ -25,9 +25,8 @@ enum PopupItemId { POPUP_ITEM_ID_USERNAME_ENTRY = -11, POPUP_ITEM_ID_CREATE_HINT = -12, POPUP_ITEM_ID_ALL_SAVED_PASSWORDS_ENTRY = -13, - POPUP_ITEM_ID_GOOGLE_PAY_BRANDING = -14, - POPUP_ITEM_ID_GENERATE_PASSWORD_ENTRY = -15, - POPUP_ITEM_ID_SHOW_ACCOUNT_CARDS = -16, + POPUP_ITEM_ID_GENERATE_PASSWORD_ENTRY = -14, + POPUP_ITEM_ID_SHOW_ACCOUNT_CARDS = -15, }; } // namespace autofill diff --git a/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc index 7ae1e1cadbc..66e412c2b9f 100644 --- a/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc +++ b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc @@ -23,7 +23,7 @@ #include "components/autofill/core/browser/webdata/autofill_entry.h" #include "components/autofill/core/browser/webdata/autofill_table.h" #include "components/autofill/core/browser/webdata/mock_autofill_webdata_backend.h" -#include "components/sync/base/hash_util.h" +#include "components/sync/base/client_tag_hash.h" #include "components/sync/model/data_batch.h" #include "components/sync/model/data_type_activation_request.h" #include "components/sync/model/metadata_batch.h" @@ -245,7 +245,7 @@ class AutocompleteSyncBridgeTest : public testing::Test { const AutofillSpecifics& specifics) { auto data = std::make_unique<EntityData>(); *data->specifics.mutable_autofill() = specifics; - data->client_tag_hash = syncer::GenerateSyncableHash( + data->client_tag_hash = syncer::ClientTagHash::FromUnhashed( syncer::AUTOFILL, bridge()->GetClientTag(*data)); return data; } @@ -293,7 +293,7 @@ class AutocompleteSyncBridgeTest : public testing::Test { private: ScopedTempDir temp_dir_; - base::test::TaskEnvironment task_environment_; + base::test::SingleThreadTaskEnvironment task_environment_; testing::NiceMock<MockAutofillWebDataBackend> backend_; AutofillTable table_; WebDatabase db_; diff --git a/chromium/components/autofill/core/browser/webdata/autofill_profile_model_type_controller.cc b/chromium/components/autofill/core/browser/webdata/autofill_profile_model_type_controller.cc index eeda7a580d9..d2489486839 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_profile_model_type_controller.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_profile_model_type_controller.cc @@ -14,11 +14,12 @@ namespace browser_sync { AutofillProfileModelTypeController::AutofillProfileModelTypeController( - std::unique_ptr<syncer::ModelTypeControllerDelegate> delegate_on_disk, + std::unique_ptr<syncer::ModelTypeControllerDelegate> + delegate_for_full_sync_mode, PrefService* pref_service, syncer::SyncService* sync_service) : ModelTypeController(syncer::AUTOFILL_PROFILE, - std::move(delegate_on_disk)), + std::move(delegate_for_full_sync_mode)), pref_service_(pref_service), sync_service_(sync_service) { pref_registrar_.Init(pref_service_); diff --git a/chromium/components/autofill/core/browser/webdata/autofill_profile_model_type_controller.h b/chromium/components/autofill/core/browser/webdata/autofill_profile_model_type_controller.h index ed684b9e63e..9ff3493d283 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_profile_model_type_controller.h +++ b/chromium/components/autofill/core/browser/webdata/autofill_profile_model_type_controller.h @@ -24,7 +24,8 @@ namespace browser_sync { class AutofillProfileModelTypeController : public syncer::ModelTypeController { public: AutofillProfileModelTypeController( - std::unique_ptr<syncer::ModelTypeControllerDelegate> delegate_on_disk, + std::unique_ptr<syncer::ModelTypeControllerDelegate> + delegate_for_full_sync_mode, PrefService* pref_service, syncer::SyncService* sync_service); ~AutofillProfileModelTypeController() override; diff --git a/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_bridge_unittest.cc b/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_bridge_unittest.cc index e0579be7e08..0ebf17dc2c8 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_bridge_unittest.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_bridge_unittest.cc @@ -28,7 +28,7 @@ #include "components/autofill/core/browser/webdata/autofill_table.h" #include "components/autofill/core/browser/webdata/mock_autofill_webdata_backend.h" #include "components/autofill/core/common/autofill_constants.h" -#include "components/sync/base/hash_util.h" +#include "components/sync/base/client_tag_hash.h" #include "components/sync/model/data_batch.h" #include "components/sync/model/data_type_activation_request.h" #include "components/sync/model/entity_data.h" @@ -296,7 +296,7 @@ class AutofillProfileSyncBridgeTest : public testing::Test { const AutofillProfileSpecifics& specifics) { auto data = std::make_unique<EntityData>(); *data->specifics.mutable_autofill_profile() = specifics; - data->client_tag_hash = syncer::GenerateSyncableHash( + data->client_tag_hash = syncer::ClientTagHash::FromUnhashed( syncer::AUTOFILL_PROFILE, bridge()->GetClientTag(*data)); return data; } @@ -321,7 +321,7 @@ class AutofillProfileSyncBridgeTest : public testing::Test { private: autofill::TestAutofillClock test_clock_; ScopedTempDir temp_dir_; - base::test::TaskEnvironment task_environment_; + base::test::SingleThreadTaskEnvironment task_environment_; testing::NiceMock<MockAutofillWebDataBackend> backend_; AutofillTable table_; WebDatabase db_; diff --git a/chromium/components/autofill/core/browser/webdata/autofill_sync_bridge_util_unittest.cc b/chromium/components/autofill/core/browser/webdata/autofill_sync_bridge_util_unittest.cc index 0ca087cdd24..997d51b187b 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_sync_bridge_util_unittest.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_sync_bridge_util_unittest.cc @@ -14,7 +14,7 @@ #include "components/autofill/core/browser/webdata/autofill_sync_bridge_test_util.h" #include "components/autofill/core/browser/webdata/autofill_table.h" #include "components/autofill/core/common/autofill_constants.h" -#include "components/sync/base/hash_util.h" +#include "components/sync/base/client_tag_hash.h" #include "components/sync/model/entity_data.h" #include "components/sync/protocol/sync.pb.h" #include "testing/gtest/include/gtest/gtest.h" @@ -50,8 +50,8 @@ std::unique_ptr<EntityData> SpecificsToEntity( const std::string& client_tag) { auto data = std::make_unique<syncer::EntityData>(); *data->specifics.mutable_autofill_wallet() = specifics; - data->client_tag_hash = - syncer::GenerateSyncableHash(syncer::AUTOFILL_WALLET_DATA, client_tag); + data->client_tag_hash = syncer::ClientTagHash::FromUnhashed( + syncer::AUTOFILL_WALLET_DATA, client_tag); return data; } diff --git a/chromium/components/autofill/core/browser/webdata/autofill_table.cc b/chromium/components/autofill/core/browser/webdata/autofill_table.cc index df8bf08bddc..c6448b452e4 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_table.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_table.cc @@ -410,7 +410,8 @@ bool AutofillTable::CreateTablesIfNecessary() { InitMaskedCreditCardsTable() && InitUnmaskedCreditCardsTable() && InitServerCardMetadataTable() && InitServerAddressesTable() && InitServerAddressMetadataTable() && InitAutofillSyncMetadataTable() && - InitModelTypeStateTable() && InitPaymentsCustomerDataTable()); + InitModelTypeStateTable() && InitPaymentsCustomerDataTable() && + InitPaymentsUPIVPATable()); } bool AutofillTable::IsSyncable() { @@ -1640,6 +1641,21 @@ bool AutofillTable::GetPaymentsCustomerData( return s.Succeeded(); } +bool AutofillTable::InsertVPA(const std::string& vpa) { + sql::Transaction transaction(db_); + if (!transaction.Begin()) + return false; + + sql::Statement insert_vpa_statement( + db_->GetUniqueStatement("INSERT INTO payments_upi_vpa (vpa) VALUES (?)")); + insert_vpa_statement.BindString(0, vpa); + insert_vpa_statement.Run(); + + transaction.Commit(); + + return db_->GetLastChangeCount() > 0; +} + bool AutofillTable::ClearAllServerData() { sql::Transaction transaction(db_); if (!transaction.Begin()) @@ -1843,36 +1859,6 @@ bool AutofillTable::RemoveOriginURLsModifiedBetween( return true; } -bool AutofillTable::GetAutofillProfilesInTrash( - std::vector<std::string>* guids) { - guids->clear(); - - sql::Statement s( - db_->GetUniqueStatement("SELECT guid FROM autofill_profiles_trash")); - - while (s.Step()) { - std::string guid = s.ColumnString(0); - guids->push_back(guid); - } - - return s.Succeeded(); -} - -bool AutofillTable::EmptyAutofillProfilesTrash() { - sql::Statement s( - db_->GetUniqueStatement("DELETE FROM autofill_profiles_trash")); - - return s.Run(); -} - -bool AutofillTable::AddAutofillGUIDToTrash(const std::string& guid) { - sql::Statement s(db_->GetUniqueStatement( - "INSERT INTO autofill_profiles_trash (guid) VALUES (?)")); - s.BindString(0, guid); - - return s.Run(); -} - bool AutofillTable::ClearAutofillProfiles() { sql::Statement s1(db_->GetUniqueStatement("DELETE FROM autofill_profiles")); @@ -2609,11 +2595,12 @@ bool AutofillTable::MigrateToVersion78AddModelTypeColumns() { "SELECT ?, storage_key, value " "FROM autofill_sync_metadata")); // Note: This uses the *wrong* ID for the ModelType - instead of - // |syncer::ModelTypeToHistogramInt|, this should be |GetKeyValueForModelType| + // |syncer::ModelTypeHistogramValue|, this should be |GetKeyValueForModelType| // aka |syncer::ModelTypeToStableIdentifier|. But at this point, fixing it // here would just make an even bigger mess. Instead, we clean this up in the // migration to version 81. See also crbug.com/895826. - insert_metadata.BindInt(0, syncer::ModelTypeToHistogramInt(syncer::AUTOFILL)); + insert_metadata.BindInt( + 0, static_cast<int>(syncer::ModelTypeHistogramValue(syncer::AUTOFILL))); // Prior to this migration, the table was a singleton, containing only one // entry with id being hard-coded to 1. @@ -2622,7 +2609,8 @@ bool AutofillTable::MigrateToVersion78AddModelTypeColumns() { "(model_type, value) SELECT ?, value " "FROM autofill_model_type_state WHERE id=1")); // Note: Like above, this uses the *wrong* ID for the ModelType. - insert_state.BindInt(0, syncer::ModelTypeToHistogramInt(syncer::AUTOFILL)); + insert_state.BindInt( + 0, static_cast<int>(syncer::ModelTypeHistogramValue(syncer::AUTOFILL))); if (!insert_metadata.Run() || !insert_state.Run()) { return false; @@ -2655,7 +2643,7 @@ bool AutofillTable::MigrateToVersion81CleanUpWrongModelTypeData() { // in trying to recover anything, since by now it'll have been redownloaded // anyway. const int bad_model_type_id = - syncer::ModelTypeToHistogramInt(syncer::AUTOFILL); + static_cast<int>(syncer::ModelTypeHistogramValue(syncer::AUTOFILL)); DCHECK_NE(bad_model_type_id, GetKeyValueForModelType(syncer::AUTOFILL)); sql::Transaction transaction(db_); @@ -3139,4 +3127,15 @@ bool AutofillTable::InitPaymentsCustomerDataTable() { return true; } +bool AutofillTable::InitPaymentsUPIVPATable() { + if (!db_->DoesTableExist("payments_upi_vpa")) { + if (!db_->Execute("CREATE TABLE payments_upi_vpa (" + "vpa VARCHAR)")) { + NOTREACHED(); + return false; + } + } + return true; +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/webdata/autofill_table.h b/chromium/components/autofill/core/browser/webdata/autofill_table.h index 0e212e999ac..ba29c015768 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_table.h +++ b/chromium/components/autofill/core/browser/webdata/autofill_table.h @@ -177,6 +177,8 @@ struct PaymentsCustomerData; // exp_month Expiration month: 1-12 // exp_year Four-digit year: 2017 // bank_name Issuer bank name of the credit card. +// cloud_token_data Opaque identifier for the cloud token associated with +// the payment instrument. // // unmasked_credit_cards // When a masked credit credit card is unmasked and the @@ -273,6 +275,11 @@ struct PaymentsCustomerData; // Contains Google Payments customer data. // // customer_id A string representing the Google Payments customer id. +// +// payments_upi_vpa Contains saved UPI/VPA payment data. +// https://en.wikipedia.org/wiki/Unified_Payments_Interface +// +// vpa_id A string representing the VPA value. class AutofillTable : public WebDatabaseTable, public syncer::SyncMetadataStore { @@ -433,6 +440,9 @@ class AutofillTable : public WebDatabaseTable, bool GetPaymentsCustomerData( std::unique_ptr<PaymentsCustomerData>* customer_data) const; + // Adds |vpa| to the saved VPA ids. + bool InsertVPA(const std::string& vpa); + // Deletes all data from the server card and profile tables. Returns true if // any data was deleted, false if not (so false means "commit not needed" // rather than "error"). @@ -466,17 +476,6 @@ class AutofillTable : public WebDatabaseTable, const base::Time& delete_end, std::vector<std::unique_ptr<AutofillProfile>>* profiles); - // Retrieves all profiles in the database that have been deleted since last - // "empty" of the trash. - bool GetAutofillProfilesInTrash(std::vector<std::string>* guids); - - // Empties the Autofill profiles "trash can". - bool EmptyAutofillProfilesTrash(); - - // Retrieves all profiles in the database that have been deleted since last - // "empty" of the trash. - bool AddAutofillGUIDToTrash(const std::string& guid); - // Clear all profiles. bool ClearAutofillProfiles(); @@ -635,6 +634,7 @@ class AutofillTable : public WebDatabaseTable, bool InitAutofillSyncMetadataTable(); bool InitModelTypeStateTable(); bool InitPaymentsCustomerDataTable(); + bool InitPaymentsUPIVPATable(); std::unique_ptr<AutofillTableEncryptor> autofill_table_encryptor_; diff --git a/chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc b/chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc index cd9547ff280..54444cbb468 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc @@ -85,14 +85,15 @@ std::ostream& operator<<(std::ostream& os, const AutofillChange& change) { namespace { typedef std::set<AutofillEntry, - bool (*)(const AutofillEntry&, const AutofillEntry&)> AutofillEntrySet; + bool (*)(const AutofillEntry&, const AutofillEntry&)> + AutofillEntrySet; typedef AutofillEntrySet::iterator AutofillEntrySetIterator; bool CompareAutofillEntries(const AutofillEntry& a, const AutofillEntry& b) { - return std::tie(a.key().name(), a.key().value(), - a.date_created(), a.date_last_used()) < - std::tie(b.key().name(), b.key().value(), - b.date_created(), b.date_last_used()); + return std::tie(a.key().name(), a.key().value(), a.date_created(), + a.date_last_used()) < + std::tie(b.key().name(), b.key().value(), b.date_created(), + b.date_last_used()); } AutofillEntry MakeAutofillEntry(const std::string& name, @@ -160,7 +161,7 @@ class AutofillTableTest : public testing::Test { }; TEST_F(AutofillTableTest, Autofill) { - Time t1 = Time::Now(); + Time t1 = AutofillClock::Now(); // Simulate the submission of a handful of entries in a field called "Name", // some more often than others. @@ -168,25 +169,25 @@ TEST_F(AutofillTableTest, Autofill) { FormFieldData field; field.name = ASCIIToUTF16("Name"); field.value = ASCIIToUTF16("Superman"); - base::Time now = base::Time::Now(); + base::Time now = AutofillClock::Now(); base::TimeDelta two_seconds = base::TimeDelta::FromSeconds(2); EXPECT_TRUE(table_->AddFormFieldValue(field, &changes)); std::vector<AutofillEntry> v; for (int i = 0; i < 5; ++i) { field.value = ASCIIToUTF16("Clark Kent"); - EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, - now + i * two_seconds)); + EXPECT_TRUE( + table_->AddFormFieldValueTime(field, &changes, now + i * two_seconds)); } for (int i = 0; i < 3; ++i) { field.value = ASCIIToUTF16("Clark Sutter"); - EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, - now + i * two_seconds)); + EXPECT_TRUE( + table_->AddFormFieldValueTime(field, &changes, now + i * two_seconds)); } for (int i = 0; i < 2; ++i) { field.name = ASCIIToUTF16("Favorite Color"); field.value = ASCIIToUTF16("Green"); - EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, - now + i * two_seconds)); + EXPECT_TRUE( + table_->AddFormFieldValueTime(field, &changes, now + i * two_seconds)); } // We have added the name Clark Kent 5 times, so count should be 5. @@ -205,8 +206,8 @@ TEST_F(AutofillTableTest, Autofill) { // in the second argument means it should return all suggestions for a name // no matter what they start with. The order that the names occur in the list // should be decreasing order by count. - EXPECT_TRUE(table_->GetFormValuesForElementName( - ASCIIToUTF16("Name"), base::string16(), &v, 6)); + EXPECT_TRUE(table_->GetFormValuesForElementName(ASCIIToUTF16("Name"), + base::string16(), &v, 6)); EXPECT_EQ(3U, v.size()); if (v.size() == 3) { EXPECT_EQ(ASCIIToUTF16("Clark Kent"), v[0].key().value()); @@ -216,8 +217,8 @@ TEST_F(AutofillTableTest, Autofill) { // If we query again limiting the list size to 1, we should only get the most // frequent entry. - EXPECT_TRUE(table_->GetFormValuesForElementName( - ASCIIToUTF16("Name"), base::string16(), &v, 1)); + EXPECT_TRUE(table_->GetFormValuesForElementName(ASCIIToUTF16("Name"), + base::string16(), &v, 1)); EXPECT_EQ(1U, v.size()); if (v.size() == 1) { EXPECT_EQ(ASCIIToUTF16("Clark Kent"), v[0].key().value()); @@ -225,8 +226,8 @@ TEST_F(AutofillTableTest, Autofill) { // Querying for suggestions given a prefix is case-insensitive, so the prefix // "cLa" shoud get suggestions for both Clarks. - EXPECT_TRUE(table_->GetFormValuesForElementName( - ASCIIToUTF16("Name"), ASCIIToUTF16("cLa"), &v, 6)); + EXPECT_TRUE(table_->GetFormValuesForElementName(ASCIIToUTF16("Name"), + ASCIIToUTF16("cLa"), &v, 6)); EXPECT_EQ(2U, v.size()); if (v.size() == 2) { EXPECT_EQ(ASCIIToUTF16("Clark Kent"), v[0].key().value()); @@ -239,18 +240,18 @@ TEST_F(AutofillTableTest, Autofill) { EXPECT_TRUE(table_->RemoveFormElementsAddedBetween(t1, Time(), &changes)); const AutofillChange kExpectedChanges[] = { - AutofillChange(AutofillChange::REMOVE, - AutofillKey(ASCIIToUTF16("Name"), - ASCIIToUTF16("Superman"))), - AutofillChange(AutofillChange::REMOVE, - AutofillKey(ASCIIToUTF16("Name"), - ASCIIToUTF16("Clark Kent"))), - AutofillChange(AutofillChange::REMOVE, - AutofillKey(ASCIIToUTF16("Name"), - ASCIIToUTF16("Clark Sutter"))), - AutofillChange(AutofillChange::REMOVE, - AutofillKey(ASCIIToUTF16("Favorite Color"), - ASCIIToUTF16("Green"))), + AutofillChange( + AutofillChange::REMOVE, + AutofillKey(ASCIIToUTF16("Name"), ASCIIToUTF16("Superman"))), + AutofillChange( + AutofillChange::REMOVE, + AutofillKey(ASCIIToUTF16("Name"), ASCIIToUTF16("Clark Kent"))), + AutofillChange( + AutofillChange::REMOVE, + AutofillKey(ASCIIToUTF16("Name"), ASCIIToUTF16("Clark Sutter"))), + AutofillChange( + AutofillChange::REMOVE, + AutofillKey(ASCIIToUTF16("Favorite Color"), ASCIIToUTF16("Green"))), }; EXPECT_EQ(base::size(kExpectedChanges), changes.size()); for (size_t i = 0; i < base::size(kExpectedChanges); ++i) { @@ -260,8 +261,8 @@ TEST_F(AutofillTableTest, Autofill) { EXPECT_EQ(0, GetAutofillEntryCount(ASCIIToUTF16("Name"), ASCIIToUTF16("Clark Kent"), db_.get())); - EXPECT_TRUE(table_->GetFormValuesForElementName( - ASCIIToUTF16("Name"), base::string16(), &v, 6)); + EXPECT_TRUE(table_->GetFormValuesForElementName(ASCIIToUTF16("Name"), + base::string16(), &v, 6)); EXPECT_EQ(0U, v.size()); // Now add some values with empty strings. @@ -282,8 +283,8 @@ TEST_F(AutofillTableTest, Autofill) { // They should be stored normally as the DB layer does not check for empty // values. v.clear(); - EXPECT_TRUE(table_->GetFormValuesForElementName( - ASCIIToUTF16("blank"), base::string16(), &v, 10)); + EXPECT_TRUE(table_->GetFormValuesForElementName(ASCIIToUTF16("blank"), + base::string16(), &v, 10)); EXPECT_EQ(4U, v.size()); } @@ -329,21 +330,16 @@ TEST_F(AutofillTableTest, Autofill_GetCountOfValuesContainedBetween) { // This test makes time comparisons that are precise to a microsecond, but the // database uses the time_t format which is only precise to a second. // Make sure we use timestamps rounded to a second. - Time begin = Time::FromTimeT(Time::Now().ToTimeT()); + Time begin = Time::FromTimeT(AutofillClock::Now().ToTimeT()); Time now = begin; TimeDelta second = TimeDelta::FromSeconds(1); struct Entry { const char* name; const char* value; - } entries[] = { - { "Alter ego", "Superman" }, - { "Name", "Superman" }, - { "Name", "Clark Kent" }, - { "Name", "Superman" }, - { "Name", "Clark Sutter" }, - { "Nomen", "Clark Kent" } - }; + } entries[] = {{"Alter ego", "Superman"}, {"Name", "Superman"}, + {"Name", "Clark Kent"}, {"Name", "Superman"}, + {"Name", "Clark Sutter"}, {"Nomen", "Clark Kent"}}; for (Entry entry : entries) { FormFieldData field; @@ -356,30 +352,29 @@ TEST_F(AutofillTableTest, Autofill_GetCountOfValuesContainedBetween) { // While the entry "Alter ego" : "Superman" is entirely contained within // the first second, the value "Superman" itself appears in another entry, // so it is not contained. - EXPECT_EQ(0, table_->GetCountOfValuesContainedBetween( - begin, begin + second)); + EXPECT_EQ(0, table_->GetCountOfValuesContainedBetween(begin, begin + second)); // No values are entirely contained within the first three seconds either // (note that the second time constraint is exclusive). - EXPECT_EQ(0, table_->GetCountOfValuesContainedBetween( - begin, begin + 3 * second)); + EXPECT_EQ( + 0, table_->GetCountOfValuesContainedBetween(begin, begin + 3 * second)); // Only "Superman" is entirely contained within the first four seconds. - EXPECT_EQ(1, table_->GetCountOfValuesContainedBetween( - begin, begin + 4 * second)); + EXPECT_EQ( + 1, table_->GetCountOfValuesContainedBetween(begin, begin + 4 * second)); // "Clark Kent" and "Clark Sutter" are contained between the first // and seventh second. - EXPECT_EQ(2, table_->GetCountOfValuesContainedBetween( - begin + second, begin + 7 * second)); + EXPECT_EQ(2, table_->GetCountOfValuesContainedBetween(begin + second, + begin + 7 * second)); // Beginning from the third second, "Clark Kent" is not contained. - EXPECT_EQ(1, table_->GetCountOfValuesContainedBetween( - begin + 3 * second, begin + 7 * second)); + EXPECT_EQ(1, table_->GetCountOfValuesContainedBetween(begin + 3 * second, + begin + 7 * second)); // We have three distinct values total. - EXPECT_EQ(3, table_->GetCountOfValuesContainedBetween( - begin, begin + 7 * second)); + EXPECT_EQ( + 3, table_->GetCountOfValuesContainedBetween(begin, begin + 7 * second)); // And we should get the same result for unlimited time interval. EXPECT_EQ(3, table_->GetCountOfValuesContainedBetween(Time(), Time::Max())); @@ -388,8 +383,8 @@ TEST_F(AutofillTableTest, Autofill_GetCountOfValuesContainedBetween) { EXPECT_EQ(3, table_->GetCountOfValuesContainedBetween(Time(), Time())); // An interval that does not fully contain any entries returns zero. - EXPECT_EQ(0, table_->GetCountOfValuesContainedBetween( - begin + second, begin + 2 * second)); + EXPECT_EQ(0, table_->GetCountOfValuesContainedBetween(begin + second, + begin + 2 * second)); // So does an interval which has no intersection with any entry. EXPECT_EQ(0, table_->GetCountOfValuesContainedBetween(Time(), begin)); @@ -397,7 +392,7 @@ TEST_F(AutofillTableTest, Autofill_GetCountOfValuesContainedBetween) { TEST_F(AutofillTableTest, Autofill_RemoveBetweenChanges) { TimeDelta one_day(TimeDelta::FromDays(1)); - Time t1 = Time::Now(); + Time t1 = AutofillClock::Now(); Time t2 = t1 + one_day; AutofillChangeList changes; @@ -410,24 +405,24 @@ TEST_F(AutofillTableTest, Autofill_RemoveBetweenChanges) { changes.clear(); EXPECT_TRUE(table_->RemoveFormElementsAddedBetween(t1, t2, &changes)); ASSERT_EQ(1U, changes.size()); - EXPECT_EQ(AutofillChange(AutofillChange::UPDATE, - AutofillKey(ASCIIToUTF16("Name"), - ASCIIToUTF16("Superman"))), + EXPECT_EQ(AutofillChange( + AutofillChange::UPDATE, + AutofillKey(ASCIIToUTF16("Name"), ASCIIToUTF16("Superman"))), changes[0]); changes.clear(); EXPECT_TRUE( table_->RemoveFormElementsAddedBetween(t2, t2 + one_day, &changes)); ASSERT_EQ(1U, changes.size()); - EXPECT_EQ(AutofillChange(AutofillChange::REMOVE, - AutofillKey(ASCIIToUTF16("Name"), - ASCIIToUTF16("Superman"))), + EXPECT_EQ(AutofillChange( + AutofillChange::REMOVE, + AutofillKey(ASCIIToUTF16("Name"), ASCIIToUTF16("Superman"))), changes[0]); } TEST_F(AutofillTableTest, Autofill_AddChanges) { TimeDelta one_day(TimeDelta::FromDays(1)); - Time t1 = Time::Now(); + Time t1 = AutofillClock::Now(); Time t2 = t1 + one_day; AutofillChangeList changes; @@ -436,18 +431,17 @@ TEST_F(AutofillTableTest, Autofill_AddChanges) { field.value = ASCIIToUTF16("Superman"); EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, t1)); ASSERT_EQ(1U, changes.size()); - EXPECT_EQ(AutofillChange(AutofillChange::ADD, - AutofillKey(ASCIIToUTF16("Name"), - ASCIIToUTF16("Superman"))), + EXPECT_EQ(AutofillChange( + AutofillChange::ADD, + AutofillKey(ASCIIToUTF16("Name"), ASCIIToUTF16("Superman"))), changes[0]); changes.clear(); - EXPECT_TRUE( - table_->AddFormFieldValueTime(field, &changes, t2)); + EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, t2)); ASSERT_EQ(1U, changes.size()); - EXPECT_EQ(AutofillChange(AutofillChange::UPDATE, - AutofillKey(ASCIIToUTF16("Name"), - ASCIIToUTF16("Superman"))), + EXPECT_EQ(AutofillChange( + AutofillChange::UPDATE, + AutofillKey(ASCIIToUTF16("Name"), ASCIIToUTF16("Superman"))), changes[0]); } @@ -489,8 +483,7 @@ TEST_F(AutofillTableTest, Autofill_GetAutofillTimestamps) { Time date_created, date_last_used; ASSERT_TRUE(table_->GetAutofillTimestamps(ASCIIToUTF16("foo"), - ASCIIToUTF16("bar"), - &date_created, + ASCIIToUTF16("bar"), &date_created, &date_last_used)); EXPECT_EQ(Time::FromTimeT(1), date_created); EXPECT_EQ(Time::FromTimeT(2), date_last_used); @@ -555,7 +548,7 @@ TEST_F(AutofillTableTest, Autofill_UpdateReplace) { } TEST_F(AutofillTableTest, Autofill_UpdateDontReplace) { - Time t = Time::Now(); + Time t = AutofillClock::Now(); AutofillEntry existing( MakeAutofillEntry("Name", "Superman", t.ToTimeT(), -1)); @@ -573,15 +566,14 @@ TEST_F(AutofillTableTest, Autofill_UpdateDontReplace) { std::vector<AutofillEntry> all_entries; ASSERT_TRUE(table_->GetAllAutofillEntries(&all_entries)); ASSERT_EQ(2U, all_entries.size()); - AutofillEntrySet expected_entries(all_entries.begin(), - all_entries.end(), + AutofillEntrySet expected_entries(all_entries.begin(), all_entries.end(), CompareAutofillEntries); EXPECT_EQ(1U, expected_entries.count(existing)); EXPECT_EQ(1U, expected_entries.count(entry)); } TEST_F(AutofillTableTest, Autofill_AddFormFieldValues) { - Time t = Time::Now(); + Time t = AutofillClock::Now(); // Add multiple values for "firstname" and "lastname" names. Test that only // first value of each gets added. Related to security issue: @@ -610,10 +602,10 @@ TEST_F(AutofillTableTest, Autofill_AddFormFieldValues) { ASSERT_EQ(2U, changes.size()); EXPECT_EQ(changes[0], AutofillChange(AutofillChange::ADD, AutofillKey(ASCIIToUTF16("firstname"), - ASCIIToUTF16("Joe")))); + ASCIIToUTF16("Joe")))); EXPECT_EQ(changes[1], AutofillChange(AutofillChange::ADD, AutofillKey(ASCIIToUTF16("lastname"), - ASCIIToUTF16("Smith")))); + ASCIIToUTF16("Smith")))); std::vector<AutofillEntry> all_entries; ASSERT_TRUE(table_->GetAllAutofillEntries(&all_entries)); @@ -641,9 +633,8 @@ TEST_F(AutofillTableTest, EXPECT_EQ(5, GetAutofillEntryCount(field.name, field.value, db_.get())); changes.clear(); - EXPECT_TRUE(table_->RemoveFormElementsAddedBetween(base::Time::FromTimeT(51), - base::Time::FromTimeT(60), - &changes)); + EXPECT_TRUE(table_->RemoveFormElementsAddedBetween( + base::Time::FromTimeT(51), base::Time::FromTimeT(60), &changes)); EXPECT_TRUE(changes.empty()); EXPECT_EQ(5, GetAutofillEntryCount(field.name, field.value, db_.get())); } @@ -669,9 +660,8 @@ TEST_F(AutofillTableTest, EXPECT_EQ(5, GetAutofillEntryCount(field.name, field.value, db_.get())); changes.clear(); - EXPECT_TRUE(table_->RemoveFormElementsAddedBetween(base::Time::FromTimeT(40), - base::Time::FromTimeT(50), - &changes)); + EXPECT_TRUE(table_->RemoveFormElementsAddedBetween( + base::Time::FromTimeT(40), base::Time::FromTimeT(50), &changes)); EXPECT_TRUE(changes.empty()); EXPECT_EQ(5, GetAutofillEntryCount(field.name, field.value, db_.get())); } @@ -697,9 +687,8 @@ TEST_F(AutofillTableTest, EXPECT_EQ(5, GetAutofillEntryCount(field.name, field.value, db_.get())); changes.clear(); - EXPECT_TRUE(table_->RemoveFormElementsAddedBetween(base::Time::FromTimeT(10), - base::Time::FromTimeT(51), - &changes)); + EXPECT_TRUE(table_->RemoveFormElementsAddedBetween( + base::Time::FromTimeT(10), base::Time::FromTimeT(51), &changes)); ASSERT_EQ(1U, changes.size()); EXPECT_EQ(AutofillChange(AutofillChange::REMOVE, AutofillKey(field.name, field.value)), @@ -728,18 +717,16 @@ TEST_F(AutofillTableTest, EXPECT_EQ(5, GetAutofillEntryCount(field.name, field.value, db_.get())); changes.clear(); - EXPECT_TRUE(table_->RemoveFormElementsAddedBetween(base::Time::FromTimeT(40), - base::Time::FromTimeT(60), - &changes)); + EXPECT_TRUE(table_->RemoveFormElementsAddedBetween( + base::Time::FromTimeT(40), base::Time::FromTimeT(60), &changes)); ASSERT_EQ(1U, changes.size()); EXPECT_EQ(AutofillChange(AutofillChange::UPDATE, AutofillKey(field.name, field.value)), changes[0]); EXPECT_EQ(4, GetAutofillEntryCount(field.name, field.value, db_.get())); base::Time date_created, date_last_used; - EXPECT_TRUE( - table_->GetAutofillTimestamps(field.name, field.value, - &date_created, &date_last_used)); + EXPECT_TRUE(table_->GetAutofillTimestamps(field.name, field.value, + &date_created, &date_last_used)); EXPECT_EQ(base::Time::FromTimeT(10), date_created); EXPECT_EQ(base::Time::FromTimeT(39), date_last_used); } @@ -765,25 +752,23 @@ TEST_F(AutofillTableTest, EXPECT_EQ(5, GetAutofillEntryCount(field.name, field.value, db_.get())); changes.clear(); - EXPECT_TRUE(table_->RemoveFormElementsAddedBetween(base::Time::FromTimeT(40), - base::Time::FromTimeT(80), - &changes)); + EXPECT_TRUE(table_->RemoveFormElementsAddedBetween( + base::Time::FromTimeT(40), base::Time::FromTimeT(80), &changes)); ASSERT_EQ(1U, changes.size()); EXPECT_EQ(AutofillChange(AutofillChange::UPDATE, AutofillKey(field.name, field.value)), changes[0]); EXPECT_EQ(2, GetAutofillEntryCount(field.name, field.value, db_.get())); base::Time date_created, date_last_used; - EXPECT_TRUE( - table_->GetAutofillTimestamps(field.name, field.value, - &date_created, &date_last_used)); + EXPECT_TRUE(table_->GetAutofillTimestamps(field.name, field.value, + &date_created, &date_last_used)); EXPECT_EQ(base::Time::FromTimeT(80), date_created); EXPECT_EQ(base::Time::FromTimeT(90), date_last_used); } TEST_F(AutofillTableTest, Autofill_RemoveFormElementsAddedBetween_OlderThan30Days) { - const base::Time kNow = base::Time::Now(); + const base::Time kNow = AutofillClock::Now(); const base::Time k29DaysOld = kNow - base::TimeDelta::FromDays(29); const base::Time k30DaysOld = kNow - base::TimeDelta::FromDays(30); const base::Time k31DaysOld = kNow - base::TimeDelta::FromDays(31); @@ -880,9 +865,9 @@ TEST_F(AutofillTableTest, AutofillProfile) { home_profile.SetClientValidityFromBitfieldValue(6); home_profile.set_is_client_validity_states_updated(true); - Time pre_creation_time = Time::Now(); + Time pre_creation_time = AutofillClock::Now(); EXPECT_TRUE(table_->AddAutofillProfile(home_profile)); - Time post_creation_time = Time::Now(); + Time post_creation_time = AutofillClock::Now(); // Get the 'Home' profile. std::unique_ptr<AutofillProfile> db_profile = @@ -907,9 +892,9 @@ TEST_F(AutofillTableTest, AutofillProfile) { ASCIIToUTF16("5678 Bottom Street")); billing_profile.SetRawInfo(ADDRESS_HOME_LINE2, ASCIIToUTF16("suite 3")); - pre_creation_time = Time::Now(); + pre_creation_time = AutofillClock::Now(); EXPECT_TRUE(table_->AddAutofillProfile(billing_profile)); - post_creation_time = Time::Now(); + post_creation_time = AutofillClock::Now(); // Get the 'Billing' profile. db_profile = table_->GetAutofillProfile(billing_profile.guid()); @@ -926,9 +911,9 @@ TEST_F(AutofillTableTest, AutofillProfile) { // Update the 'Billing' profile, name only. billing_profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Jane")); - Time pre_modification_time = Time::Now(); + Time pre_modification_time = AutofillClock::Now(); EXPECT_TRUE(table_->UpdateAutofillProfile(billing_profile)); - Time post_modification_time = Time::Now(); + Time post_modification_time = AutofillClock::Now(); db_profile = table_->GetAutofillProfile(billing_profile.guid()); ASSERT_TRUE(db_profile); EXPECT_EQ(billing_profile, *db_profile); @@ -937,10 +922,8 @@ TEST_F(AutofillTableTest, AutofillProfile) { s_billing_updated.BindString(0, billing_profile.guid()); ASSERT_TRUE(s_billing_updated.is_valid()); ASSERT_TRUE(s_billing_updated.Step()); - EXPECT_GE(s_billing_updated.ColumnInt64(0), - pre_modification_time.ToTimeT()); - EXPECT_LE(s_billing_updated.ColumnInt64(0), - post_modification_time.ToTimeT()); + EXPECT_GE(s_billing_updated.ColumnInt64(0), pre_modification_time.ToTimeT()); + EXPECT_LE(s_billing_updated.ColumnInt64(0), post_modification_time.ToTimeT()); EXPECT_FALSE(s_billing_updated.Step()); // Update the 'Billing' profile with non-default data. The specific values are @@ -965,9 +948,9 @@ TEST_F(AutofillTableTest, AutofillProfile) { billing_profile.SetClientValidityFromBitfieldValue(54); billing_profile.set_is_client_validity_states_updated(true); - Time pre_modification_time_2 = Time::Now(); + Time pre_modification_time_2 = AutofillClock::Now(); EXPECT_TRUE(table_->UpdateAutofillProfile(billing_profile)); - Time post_modification_time_2 = Time::Now(); + Time post_modification_time_2 = AutofillClock::Now(); db_profile = table_->GetAutofillProfile(billing_profile.guid()); ASSERT_TRUE(db_profile); EXPECT_EQ(billing_profile, *db_profile); @@ -989,87 +972,6 @@ TEST_F(AutofillTableTest, AutofillProfile) { EXPECT_FALSE(db_profile); } -TEST_F(AutofillTableTest, AutofillProfileTrash) { - std::vector<std::string> guids; - table_->GetAutofillProfilesInTrash(&guids); - EXPECT_TRUE(guids.empty()); - - ASSERT_TRUE(table_->AddAutofillGUIDToTrash( - "00000000-0000-0000-0000-000000000000")); - ASSERT_TRUE(table_->AddAutofillGUIDToTrash( - "00000000-0000-0000-0000-000000000001")); - ASSERT_TRUE(table_->GetAutofillProfilesInTrash(&guids)); - EXPECT_EQ(2UL, guids.size()); - EXPECT_EQ("00000000-0000-0000-0000-000000000000", guids[0]); - EXPECT_EQ("00000000-0000-0000-0000-000000000001", guids[1]); - - ASSERT_TRUE(table_->EmptyAutofillProfilesTrash()); - ASSERT_TRUE(table_->GetAutofillProfilesInTrash(&guids)); - EXPECT_TRUE(guids.empty()); -} - -TEST_F(AutofillTableTest, AutofillProfileTrashInteraction) { - std::vector<std::string> guids; - table_->GetAutofillProfilesInTrash(&guids); - EXPECT_TRUE(guids.empty()); - - AutofillProfile profile; - profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); - profile.SetRawInfo(NAME_MIDDLE, ASCIIToUTF16("Q.")); - profile.SetRawInfo(NAME_LAST, ASCIIToUTF16("Smith")); - profile.SetRawInfo(EMAIL_ADDRESS, ASCIIToUTF16("js@smith.xyz")); - profile.SetRawInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("1 Main St")); - profile.SetRawInfo(ADDRESS_HOME_CITY, ASCIIToUTF16("Los Angeles")); - profile.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("CA")); - profile.SetRawInfo(ADDRESS_HOME_ZIP, ASCIIToUTF16("90025")); - profile.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("US")); - - // Mark this profile as in the trash. This stops |AddAutofillProfile| from - // adding it. - EXPECT_TRUE(table_->AddAutofillGUIDToTrash(profile.guid())); - EXPECT_TRUE(table_->AddAutofillProfile(profile)); - std::unique_ptr<AutofillProfile> added_profile = - table_->GetAutofillProfile(profile.guid()); - EXPECT_FALSE(added_profile); - - // Add the profile for real this time. - EXPECT_TRUE(table_->EmptyAutofillProfilesTrash()); - EXPECT_TRUE(table_->GetAutofillProfilesInTrash(&guids)); - EXPECT_TRUE(guids.empty()); - EXPECT_TRUE(table_->AddAutofillProfile(profile)); - added_profile = table_->GetAutofillProfile(profile.guid()); - EXPECT_TRUE(added_profile); - - // Mark this profile as in the trash. This stops |UpdateAutofillProfileMulti| - // from updating it. In normal operation a profile should not be both in the - // trash and in the profiles table simultaneously. - EXPECT_TRUE(table_->AddAutofillGUIDToTrash(profile.guid())); - profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Jane")); - EXPECT_TRUE(table_->UpdateAutofillProfile(profile)); - std::unique_ptr<AutofillProfile> updated_profile = - table_->GetAutofillProfile(profile.guid()); - EXPECT_TRUE(updated_profile); - EXPECT_EQ(ASCIIToUTF16("John"), updated_profile->GetRawInfo(NAME_FIRST)); - - // Try to delete the trashed profile. This stops |RemoveAutofillProfile| from - // deleting it. In normal operation deletion is done by migration step, and - // removal from trash is done by |WebDataService|. |RemoveAutofillProfile| - // does remove the item from the trash if it is found however, so that if - // other clients remove it (via Sync say) then it is gone and doesn't need to - // be processed further by |WebDataService|. - EXPECT_TRUE(table_->RemoveAutofillProfile(profile.guid())); - std::unique_ptr<AutofillProfile> removed_profile = - table_->GetAutofillProfile(profile.guid()); - EXPECT_TRUE(removed_profile); - EXPECT_FALSE(table_->IsAutofillGUIDInTrash(profile.guid())); - - // Check that emptying the trash now allows removal to occur. - EXPECT_TRUE(table_->EmptyAutofillProfilesTrash()); - EXPECT_TRUE(table_->RemoveAutofillProfile(profile.guid())); - removed_profile = table_->GetAutofillProfile(profile.guid()); - EXPECT_FALSE(removed_profile); -} - TEST_F(AutofillTableTest, CreditCard) { // Add a 'Work' credit card. CreditCard work_creditcard; @@ -1082,9 +984,9 @@ TEST_F(AutofillTableTest, CreditCard) { work_creditcard.SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, ASCIIToUTF16("2013")); - Time pre_creation_time = Time::Now(); + Time pre_creation_time = AutofillClock::Now(); EXPECT_TRUE(table_->AddCreditCard(work_creditcard)); - Time post_creation_time = Time::Now(); + Time post_creation_time = AutofillClock::Now(); // Get the 'Work' credit card. std::unique_ptr<CreditCard> db_creditcard = @@ -1113,9 +1015,9 @@ TEST_F(AutofillTableTest, CreditCard) { target_creditcard.SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, ASCIIToUTF16("2012")); - pre_creation_time = Time::Now(); + pre_creation_time = AutofillClock::Now(); EXPECT_TRUE(table_->AddCreditCard(target_creditcard)); - post_creation_time = Time::Now(); + post_creation_time = AutofillClock::Now(); db_creditcard = table_->GetCreditCard(target_creditcard.guid()); ASSERT_TRUE(db_creditcard); EXPECT_EQ(target_creditcard, *db_creditcard); @@ -1134,9 +1036,9 @@ TEST_F(AutofillTableTest, CreditCard) { target_creditcard.set_origin("Interactive Autofill dialog"); target_creditcard.SetRawInfo(CREDIT_CARD_NAME_FULL, ASCIIToUTF16("Charles Grady")); - Time pre_modification_time = Time::Now(); + Time pre_modification_time = AutofillClock::Now(); EXPECT_TRUE(table_->UpdateCreditCard(target_creditcard)); - Time post_modification_time = Time::Now(); + Time post_modification_time = AutofillClock::Now(); db_creditcard = table_->GetCreditCard(target_creditcard.guid()); ASSERT_TRUE(db_creditcard); EXPECT_EQ(target_creditcard, *db_creditcard); @@ -1194,7 +1096,7 @@ TEST_F(AutofillTableTest, UpdateAutofillProfile) { table_->AddAutofillProfile(profile); // Set a mocked value for the profile's creation time. - const time_t kMockCreationDate = Time::Now().ToTimeT() - 13; + const time_t kMockCreationDate = AutofillClock::Now().ToTimeT() - 13; sql::Statement s_mock_creation_date( db_->GetSQLConnection()->GetUniqueStatement( "UPDATE autofill_profiles SET date_modified = ?")); @@ -1231,7 +1133,7 @@ TEST_F(AutofillTableTest, UpdateAutofillProfile) { EXPECT_FALSE(s_updated.Step()); // Set a mocked value for the profile's modification time. - const time_t mock_modification_date = Time::Now().ToTimeT() - 7; + const time_t mock_modification_date = AutofillClock::Now().ToTimeT() - 7; sql::Statement s_mock_modification_date( db_->GetSQLConnection()->GetUniqueStatement( "UPDATE autofill_profiles SET date_modified = ?")); @@ -1265,7 +1167,7 @@ TEST_F(AutofillTableTest, UpdateCreditCard) { table_->AddCreditCard(credit_card); // Set a mocked value for the credit card's creation time. - const time_t kMockCreationDate = Time::Now().ToTimeT() - 13; + const time_t kMockCreationDate = AutofillClock::Now().ToTimeT() - 13; sql::Statement s_mock_creation_date( db_->GetSQLConnection()->GetUniqueStatement( "UPDATE credit_cards SET date_modified = ?")); @@ -1302,7 +1204,7 @@ TEST_F(AutofillTableTest, UpdateCreditCard) { EXPECT_FALSE(s_updated.Step()); // Set a mocked value for the credit card's modification time. - const time_t mock_modification_date = Time::Now().ToTimeT() - 7; + const time_t mock_modification_date = AutofillClock::Now().ToTimeT() - 7; sql::Statement s_mock_modification_date( db_->GetSQLConnection()->GetUniqueStatement( "UPDATE credit_cards SET date_modified = ?")); @@ -1344,7 +1246,7 @@ TEST_F(AutofillTableTest, UpdateProfileOriginOnly) { table_->AddAutofillProfile(profile); // Set a mocked value for the profile's creation time. - const time_t kMockCreationDate = Time::Now().ToTimeT() - 13; + const time_t kMockCreationDate = AutofillClock::Now().ToTimeT() - 13; sql::Statement s_mock_creation_date( db_->GetSQLConnection()->GetUniqueStatement( "UPDATE autofill_profiles SET date_modified = ?")); @@ -1391,7 +1293,7 @@ TEST_F(AutofillTableTest, UpdateCreditCardOriginOnly) { table_->AddCreditCard(credit_card); // Set a mocked value for the credit card's creation time. - const time_t kMockCreationDate = Time::Now().ToTimeT() - 13; + const time_t kMockCreationDate = AutofillClock::Now().ToTimeT() - 13; sql::Statement s_mock_creation_date( db_->GetSQLConnection()->GetUniqueStatement( "UPDATE credit_cards SET date_modified = ?")); @@ -1725,8 +1627,8 @@ TEST_F(AutofillTableTest, RemoveOriginURLsModifiedBetween) { // Remove all origin URLs set in the bounded time range [21,27). std::vector<std::unique_ptr<AutofillProfile>> profiles; - table_->RemoveOriginURLsModifiedBetween( - Time::FromTimeT(21), Time::FromTimeT(27), &profiles); + table_->RemoveOriginURLsModifiedBetween(Time::FromTimeT(21), + Time::FromTimeT(27), &profiles); ASSERT_EQ(1UL, profiles.size()); EXPECT_EQ("00000000-0000-0000-0000-000000000001", profiles[0]->guid()); sql::Statement s_autofill_profiles_bounded( @@ -1751,8 +1653,7 @@ TEST_F(AutofillTableTest, RemoveOriginURLsModifiedBetween) { EXPECT_EQ(std::string(), s_credit_cards_bounded.ColumnString(1)); ASSERT_TRUE(s_credit_cards_bounded.Step()); EXPECT_EQ(27, s_credit_cards_bounded.ColumnInt64(0)); - EXPECT_EQ("https://www.example.com/", - s_credit_cards_bounded.ColumnString(1)); + EXPECT_EQ("https://www.example.com/", s_credit_cards_bounded.ColumnString(1)); ASSERT_TRUE(s_credit_cards_bounded.Step()); EXPECT_EQ(37, s_credit_cards_bounded.ColumnInt64(0)); EXPECT_EQ(kSettingsOrigin, s_credit_cards_bounded.ColumnString(1)); @@ -1774,9 +1675,8 @@ TEST_F(AutofillTableTest, RemoveOriginURLsModifiedBetween) { ASSERT_TRUE(s_autofill_profiles_all.Step()); EXPECT_EQ(31, s_autofill_profiles_all.ColumnInt64(0)); EXPECT_EQ(kSettingsOrigin, s_autofill_profiles_all.ColumnString(1)); - sql::Statement s_credit_cards_all( - db_->GetSQLConnection()->GetUniqueStatement( - "SELECT date_modified, origin FROM credit_cards")); + sql::Statement s_credit_cards_all(db_->GetSQLConnection()->GetUniqueStatement( + "SELECT date_modified, origin FROM credit_cards")); ASSERT_TRUE(s_credit_cards_all.is_valid()); ASSERT_TRUE(s_credit_cards_all.Step()); EXPECT_EQ(17, s_credit_cards_all.ColumnInt64(0)); @@ -1798,19 +1698,19 @@ TEST_F(AutofillTableTest, Autofill_GetAllAutofillEntries_NoResults) { TEST_F(AutofillTableTest, Autofill_GetAllAutofillEntries_OneResult) { AutofillChangeList changes; - std::map<std::string, std::vector<Time> > name_value_times_map; + std::map<std::string, std::vector<Time>> name_value_times_map; time_t start = 0; std::vector<Time> timestamps1; FormFieldData field; field.name = ASCIIToUTF16("Name"); field.value = ASCIIToUTF16("Superman"); - EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, - Time::FromTimeT(start))); + EXPECT_TRUE( + table_->AddFormFieldValueTime(field, &changes, Time::FromTimeT(start))); timestamps1.push_back(Time::FromTimeT(start)); std::string key1("NameSuperman"); name_value_times_map.insert( - std::pair<std::string, std::vector<Time> >(key1, timestamps1)); + std::pair<std::string, std::vector<Time>>(key1, timestamps1)); AutofillEntrySet expected_entries(CompareAutofillEntries); AutofillKey ak1(ASCIIToUTF16("Name"), ASCIIToUTF16("Superman")); @@ -1828,30 +1728,30 @@ TEST_F(AutofillTableTest, Autofill_GetAllAutofillEntries_OneResult) { TEST_F(AutofillTableTest, Autofill_GetAllAutofillEntries_TwoDistinct) { AutofillChangeList changes; - std::map<std::string, std::vector<Time> > name_value_times_map; + std::map<std::string, std::vector<Time>> name_value_times_map; time_t start = 0; std::vector<Time> timestamps1; FormFieldData field; field.name = ASCIIToUTF16("Name"); field.value = ASCIIToUTF16("Superman"); - EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, - Time::FromTimeT(start))); + EXPECT_TRUE( + table_->AddFormFieldValueTime(field, &changes, Time::FromTimeT(start))); timestamps1.push_back(Time::FromTimeT(start)); std::string key1("NameSuperman"); name_value_times_map.insert( - std::pair<std::string, std::vector<Time> >(key1, timestamps1)); + std::pair<std::string, std::vector<Time>>(key1, timestamps1)); ++start; std::vector<Time> timestamps2; field.name = ASCIIToUTF16("Name"); field.value = ASCIIToUTF16("Clark Kent"); - EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, - Time::FromTimeT(start))); + EXPECT_TRUE( + table_->AddFormFieldValueTime(field, &changes, Time::FromTimeT(start))); timestamps2.push_back(Time::FromTimeT(start)); std::string key2("NameClark Kent"); name_value_times_map.insert( - std::pair<std::string, std::vector<Time> >(key2, timestamps2)); + std::pair<std::string, std::vector<Time>>(key2, timestamps2)); AutofillEntrySet expected_entries(CompareAutofillEntries); AutofillKey ak1(ASCIIToUTF16("Name"), ASCIIToUTF16("Superman")); @@ -1872,7 +1772,7 @@ TEST_F(AutofillTableTest, Autofill_GetAllAutofillEntries_TwoDistinct) { TEST_F(AutofillTableTest, Autofill_GetAllAutofillEntries_TwoSame) { AutofillChangeList changes; - std::map<std::string, std::vector<Time> > name_value_times_map; + std::map<std::string, std::vector<Time>> name_value_times_map; std::vector<Time> timestamps; time_t start = 0; @@ -1880,14 +1780,14 @@ TEST_F(AutofillTableTest, Autofill_GetAllAutofillEntries_TwoSame) { FormFieldData field; field.name = ASCIIToUTF16("Name"); field.value = ASCIIToUTF16("Superman"); - EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, - Time::FromTimeT(start))); + EXPECT_TRUE( + table_->AddFormFieldValueTime(field, &changes, Time::FromTimeT(start))); timestamps.push_back(Time::FromTimeT(start)); } std::string key("NameSuperman"); name_value_times_map.insert( - std::pair<std::string, std::vector<Time> >(key, timestamps)); + std::pair<std::string, std::vector<Time>>(key, timestamps)); AutofillEntrySet expected_entries(CompareAutofillEntries); AutofillKey ak1(ASCIIToUTF16("Name"), ASCIIToUTF16("Superman")); @@ -1980,8 +1880,7 @@ TEST_F(AutofillTableTest, SetGetServerCards) { inputs[0].SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, ASCIIToUTF16("2020")); inputs[0].SetRawInfo(CREDIT_CARD_NUMBER, ASCIIToUTF16("4111111111111111")); - inputs.push_back( - CreditCard(CreditCard::MASKED_SERVER_CARD, "b456")); + inputs.push_back(CreditCard(CreditCard::MASKED_SERVER_CARD, "b456")); inputs[1].SetRawInfo(CREDIT_CARD_NAME_FULL, ASCIIToUTF16("Rick Roman")); inputs[1].SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("12")); inputs[1].SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, ASCIIToUTF16("1997")); @@ -2019,7 +1918,7 @@ TEST_F(AutofillTableTest, SetGetRemoveServerCardMetadata) { AutofillMetadata input; input.id = "server id"; input.use_count = 50; - input.use_date = Time::Now(); + input.use_date = AutofillClock::Now(); input.billing_address_id = "billing id"; EXPECT_TRUE(table_->AddServerCardMetadata(input)); @@ -2042,7 +1941,7 @@ TEST_F(AutofillTableTest, SetGetRemoveServerAddressMetadata) { AutofillMetadata input; input.id = "server id"; input.use_count = 50; - input.use_date = Time::Now(); + input.use_date = AutofillClock::Now(); input.has_converted = true; table_->AddServerAddressMetadata(input); @@ -2065,7 +1964,7 @@ TEST_F(AutofillTableTest, AddUpdateServerAddressMetadata) { AutofillMetadata input; input.id = "server id"; input.use_count = 50; - input.use_date = Time::Now(); + input.use_date = AutofillClock::Now(); input.has_converted = true; ASSERT_TRUE(table_->AddServerAddressMetadata(input)); @@ -2096,7 +1995,7 @@ TEST_F(AutofillTableTest, AddUpdateServerCardMetadata) { AutofillMetadata input; input.id = "server id"; input.use_count = 50; - input.use_date = Time::Now(); + input.use_date = AutofillClock::Now(); input.billing_address_id = "billing id"; ASSERT_TRUE(table_->AddServerCardMetadata(input)); @@ -2192,7 +2091,7 @@ TEST_F(AutofillTableTest, RemoveWrongServerCardMetadata) { AutofillMetadata input; input.id = "server id"; input.use_count = 50; - input.use_date = Time::Now(); + input.use_date = AutofillClock::Now(); input.billing_address_id = "billing id"; table_->AddServerCardMetadata(input); @@ -2261,7 +2160,7 @@ TEST_F(AutofillTableTest, SetServerCardsData_ExistingMetadata) { AutofillMetadata input; input.id = "server id"; input.use_count = 50; - input.use_date = Time::Now(); + input.use_date = AutofillClock::Now(); input.billing_address_id = "billing id"; table_->AddServerCardMetadata(input); @@ -2318,7 +2217,7 @@ TEST_F(AutofillTableTest, SetServerAddressesData_ExistingMetadata) { AutofillMetadata input; input.id = "server id"; input.use_count = 50; - input.use_date = Time::Now(); + input.use_date = AutofillClock::Now(); input.has_converted = true; table_->AddServerAddressMetadata(input); @@ -2340,7 +2239,7 @@ TEST_F(AutofillTableTest, RemoveWrongServerAddressMetadata) { AutofillMetadata input; input.id = "server id"; input.use_count = 50; - input.use_date = Time::Now(); + input.use_date = AutofillClock::Now(); input.has_converted = true; table_->AddServerAddressMetadata(input); @@ -2371,8 +2270,7 @@ TEST_F(AutofillTableTest, MaskUnmaskServerCards) { // Unmask the number. The full number should be available. base::string16 full_number(ASCIIToUTF16("4111111111111111")); - ASSERT_TRUE(table_->UnmaskServerCreditCard(inputs[0], - full_number)); + ASSERT_TRUE(table_->UnmaskServerCreditCard(inputs[0], full_number)); std::vector<std::unique_ptr<CreditCard>> outputs; table_->GetServerCreditCards(&outputs); @@ -2601,7 +2499,7 @@ TEST_F(AutofillTableTest, SetServerProfileUpdateUsageStats) { // Update the usage stats; make sure they're reflected in GetServerProfiles. inputs.back().set_use_count(4U); - inputs.back().set_use_date(base::Time::Now()); + inputs.back().set_use_date(AutofillClock::Now()); table_->UpdateServerAddressMetadata(inputs.back()); table_->GetServerProfiles(&outputs); ASSERT_EQ(1u, outputs.size()); @@ -2627,7 +2525,7 @@ TEST_F(AutofillTableTest, SetServerProfileUpdateUsageStats) { TEST_F(AutofillTableTest, DeleteUnmaskedCard) { // This isn't the exact unmasked time, since the database will use the // current time that it is called. The code below has to be approximate. - base::Time unmasked_time = base::Time::Now(); + base::Time unmasked_time = AutofillClock::Now(); // Add a masked card. base::string16 masked_number = ASCIIToUTF16("1111"); @@ -2666,7 +2564,7 @@ TEST_F(AutofillTableTest, DeleteUnmaskedCard) { // Delete data in the range of the last 24 hours. // Fudge |now| to make sure it's strictly greater than the |now| that // the database uses. - base::Time now = base::Time::Now() + base::TimeDelta::FromSeconds(1); + base::Time now = AutofillClock::Now() + base::TimeDelta::FromSeconds(1); ASSERT_TRUE(table_->RemoveAutofillDataModifiedBetween( now - base::TimeDelta::FromDays(1), now, &profiles, &credit_cards)); @@ -2775,7 +2673,7 @@ TEST_P(GetFormValuesTest, GetFormValuesForElementName_SubstringMatchEnabled) { << "suggestion = " << test_case.field_suggestion[0] << ", contents = " << test_case.field_contents); - Time t1 = Time::Now(); + Time t1 = AutofillClock::Now(); // Simulate the submission of a handful of entries in a field called "Name". AutofillChangeList changes; @@ -3102,4 +3000,16 @@ TEST_F(AutofillTableTest, RemoveOrphanAutofillTableRows) { EXPECT_FALSE(s_autofill_profile_phones.Step()); } +TEST_F(AutofillTableTest, VPA) { + EXPECT_TRUE(table_->InsertVPA("name@indianbank")); + + sql::Statement s_inspect(db_->GetSQLConnection()->GetUniqueStatement( + "SELECT vpa FROM payments_upi_vpa")); + + ASSERT_TRUE(s_inspect.is_valid()); + ASSERT_TRUE(s_inspect.Step()); + EXPECT_GE(s_inspect.ColumnString(0), "name@indianbank"); + EXPECT_FALSE(s_inspect.Step()); +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge_unittest.cc b/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge_unittest.cc index eeae78ff20d..0f78f82f872 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge_unittest.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge_unittest.cc @@ -29,7 +29,7 @@ #include "components/autofill/core/browser/webdata/autofill_table.h" #include "components/autofill/core/browser/webdata/mock_autofill_webdata_backend.h" #include "components/autofill/core/common/autofill_constants.h" -#include "components/sync/base/hash_util.h" +#include "components/sync/base/client_tag_hash.h" #include "components/sync/model/data_batch.h" #include "components/sync/model/entity_data.h" #include "components/sync/model/mock_model_type_change_processor.h" @@ -353,7 +353,7 @@ class AutofillWalletMetadataSyncBridgeTest : public testing::Test { bool is_deleted = false) { auto data = std::make_unique<EntityData>(); *data->specifics.mutable_wallet_metadata() = specifics; - data->client_tag_hash = syncer::GenerateSyncableHash( + data->client_tag_hash = syncer::ClientTagHash::FromUnhashed( syncer::AUTOFILL_WALLET_METADATA, bridge()->GetClientTag(*data)); if (is_deleted) { // Specifics had to be set in order to generate the client tag. Since @@ -451,7 +451,7 @@ class AutofillWalletMetadataSyncBridgeTest : public testing::Test { int response_version = 0; autofill::TestAutofillClock test_clock_; ScopedTempDir temp_dir_; - base::test::TaskEnvironment task_environment_; + base::test::SingleThreadTaskEnvironment task_environment_; testing::NiceMock<MockAutofillWebDataBackend> backend_; AutofillTable table_; WebDatabase db_; diff --git a/chromium/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge_unittest.cc b/chromium/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge_unittest.cc index 50741416fb7..12e7e536098 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge_unittest.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge_unittest.cc @@ -31,7 +31,7 @@ #include "components/autofill/core/browser/webdata/autofill_webdata_backend.h" #include "components/autofill/core/browser/webdata/mock_autofill_webdata_backend.h" #include "components/autofill/core/common/autofill_constants.h" -#include "components/sync/base/hash_util.h" +#include "components/sync/base/client_tag_hash.h" #include "components/sync/driver/sync_driver_switches.h" #include "components/sync/model/entity_data.h" #include "components/sync/model/mock_model_type_change_processor.h" @@ -269,7 +269,7 @@ class AutofillWalletSyncBridgeTest : public testing::Test { const AutofillWalletSpecifics& specifics) { auto data = std::make_unique<EntityData>(); *data->specifics.mutable_autofill_wallet() = specifics; - data->client_tag_hash = syncer::GenerateSyncableHash( + data->client_tag_hash = syncer::ClientTagHash::FromUnhashed( syncer::AUTOFILL_WALLET_DATA, bridge()->GetClientTag(*data)); return data; } @@ -307,7 +307,7 @@ class AutofillWalletSyncBridgeTest : public testing::Test { private: autofill::TestAutofillClock test_clock_; ScopedTempDir temp_dir_; - base::test::TaskEnvironment task_environment_; + base::test::SingleThreadTaskEnvironment task_environment_; NiceMock<MockAutofillWebDataBackend> backend_; AutofillTable table_; WebDatabase db_; diff --git a/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc b/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc index 3573e2a2fc1..f7f343a4b7c 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc @@ -117,10 +117,6 @@ void AutofillWebDataBackendImpl::NotifyOfCreditCardChanged( void AutofillWebDataBackendImpl::NotifyOfMultipleAutofillChanges() { DCHECK(owning_task_runner()->RunsTasksInCurrentSequence()); - // DB sequence notification. - for (auto& db_observer : db_observer_list_) - db_observer.AutofillMultipleChangedBySync(); - // UI sequence notification. ui_task_runner_->PostTask(FROM_HERE, on_changed_callback_); } @@ -157,11 +153,12 @@ void AutofillWebDataBackendImpl::ResetUserData() { } WebDatabase::State AutofillWebDataBackendImpl::AddFormElements( - const std::vector<FormFieldData>& fields, WebDatabase* db) { + const std::vector<FormFieldData>& fields, + WebDatabase* db) { DCHECK(owning_task_runner()->RunsTasksInCurrentSequence()); AutofillChangeList changes; - if (!AutofillTable::FromWebDatabase(db)->AddFormFieldValues( - fields, &changes)) { + if (!AutofillTable::FromWebDatabase(db)->AddFormFieldValues(fields, + &changes)) { NOTREACHED(); return WebDatabase::COMMIT_NOT_NEEDED; } @@ -211,7 +208,9 @@ WebDatabase::State AutofillWebDataBackendImpl::RemoveFormElementsAddedBetween( } WebDatabase::State AutofillWebDataBackendImpl::RemoveFormValueForElementName( - const base::string16& name, const base::string16& value, WebDatabase* db) { + const base::string16& name, + const base::string16& value, + WebDatabase* db) { DCHECK(owning_task_runner()->RunsTasksInCurrentSequence()); if (AutofillTable::FromWebDatabase(db)->RemoveFormElement(name, value)) { @@ -229,7 +228,8 @@ WebDatabase::State AutofillWebDataBackendImpl::RemoveFormValueForElementName( } WebDatabase::State AutofillWebDataBackendImpl::AddAutofillProfile( - const AutofillProfile& profile, WebDatabase* db) { + const AutofillProfile& profile, + WebDatabase* db) { DCHECK(owning_task_runner()->RunsTasksInCurrentSequence()); if (!AutofillTable::FromWebDatabase(db)->AddAutofillProfile(profile)) { NOTREACHED(); @@ -237,8 +237,8 @@ WebDatabase::State AutofillWebDataBackendImpl::AddAutofillProfile( } // Send GUID-based notification. - AutofillProfileChange change( - AutofillProfileChange::ADD, profile.guid(), &profile); + AutofillProfileChange change(AutofillProfileChange::ADD, profile.guid(), + &profile); for (auto& db_observer : db_observer_list_) db_observer.AutofillProfileChanged(change); @@ -253,7 +253,8 @@ WebDatabase::State AutofillWebDataBackendImpl::AddAutofillProfile( } WebDatabase::State AutofillWebDataBackendImpl::UpdateAutofillProfile( - const AutofillProfile& profile, WebDatabase* db) { + const AutofillProfile& profile, + WebDatabase* db) { DCHECK(owning_task_runner()->RunsTasksInCurrentSequence()); // Only perform the update if the profile exists. It is currently // valid to try to update a missing profile. We simply drop the write and @@ -269,8 +270,8 @@ WebDatabase::State AutofillWebDataBackendImpl::UpdateAutofillProfile( } // Send GUID-based notification. - AutofillProfileChange change( - AutofillProfileChange::UPDATE, profile.guid(), &profile); + AutofillProfileChange change(AutofillProfileChange::UPDATE, profile.guid(), + &profile); for (auto& db_observer : db_observer_list_) db_observer.AutofillProfileChanged(change); @@ -285,7 +286,8 @@ WebDatabase::State AutofillWebDataBackendImpl::UpdateAutofillProfile( } WebDatabase::State AutofillWebDataBackendImpl::RemoveAutofillProfile( - const std::string& guid, WebDatabase* db) { + const std::string& guid, + WebDatabase* db) { DCHECK(owning_task_runner()->RunsTasksInCurrentSequence()); std::unique_ptr<AutofillProfile> profile = AutofillTable::FromWebDatabase(db)->GetAutofillProfile(guid); @@ -352,8 +354,9 @@ AutofillWebDataBackendImpl::GetCountOfValuesContainedBetween( const base::Time& end, WebDatabase* db) { DCHECK(owning_task_runner()->RunsTasksInCurrentSequence()); - int value = AutofillTable::FromWebDatabase(db) - ->GetCountOfValuesContainedBetween(begin, end); + int value = + AutofillTable::FromWebDatabase(db)->GetCountOfValuesContainedBetween( + begin, end); return std::unique_ptr<WDTypedResult>( new WDResult<int>(AUTOFILL_VALUE_RESULT, value)); } @@ -362,15 +365,16 @@ WebDatabase::State AutofillWebDataBackendImpl::UpdateAutofillEntries( const std::vector<AutofillEntry>& autofill_entries, WebDatabase* db) { DCHECK(owning_task_runner()->RunsTasksInCurrentSequence()); - if (!AutofillTable::FromWebDatabase(db) - ->UpdateAutofillEntries(autofill_entries)) + if (!AutofillTable::FromWebDatabase(db)->UpdateAutofillEntries( + autofill_entries)) return WebDatabase::COMMIT_NOT_NEEDED; return WebDatabase::COMMIT_NEEDED; } WebDatabase::State AutofillWebDataBackendImpl::AddCreditCard( - const CreditCard& credit_card, WebDatabase* db) { + const CreditCard& credit_card, + WebDatabase* db) { DCHECK(owning_task_runner()->RunsTasksInCurrentSequence()); if (!AutofillTable::FromWebDatabase(db)->AddCreditCard(credit_card)) { NOTREACHED(); @@ -385,7 +389,8 @@ WebDatabase::State AutofillWebDataBackendImpl::AddCreditCard( } WebDatabase::State AutofillWebDataBackendImpl::UpdateCreditCard( - const CreditCard& credit_card, WebDatabase* db) { + const CreditCard& credit_card, + WebDatabase* db) { DCHECK(owning_task_runner()->RunsTasksInCurrentSequence()); // It is currently valid to try to update a missing profile. We simply drop // the write and the caller will detect this on the next refresh. @@ -407,7 +412,8 @@ WebDatabase::State AutofillWebDataBackendImpl::UpdateCreditCard( } WebDatabase::State AutofillWebDataBackendImpl::RemoveCreditCard( - const std::string& guid, WebDatabase* db) { + const std::string& guid, + WebDatabase* db) { DCHECK(owning_task_runner()->RunsTasksInCurrentSequence()); std::unique_ptr<CreditCard> card = AutofillTable::FromWebDatabase(db)->GetCreditCard(guid); @@ -470,16 +476,15 @@ WebDatabase::State AutofillWebDataBackendImpl::UnmaskServerCreditCard( const base::string16& full_number, WebDatabase* db) { DCHECK(owning_task_runner()->RunsTasksInCurrentSequence()); - if (AutofillTable::FromWebDatabase(db)->UnmaskServerCreditCard( - card, full_number)) + if (AutofillTable::FromWebDatabase(db)->UnmaskServerCreditCard(card, + full_number)) return WebDatabase::COMMIT_NEEDED; return WebDatabase::COMMIT_NOT_NEEDED; } -WebDatabase::State - AutofillWebDataBackendImpl::MaskServerCreditCard( - const std::string& id, - WebDatabase* db) { +WebDatabase::State AutofillWebDataBackendImpl::MaskServerCreditCard( + const std::string& id, + WebDatabase* db) { DCHECK(owning_task_runner()->RunsTasksInCurrentSequence()); if (AutofillTable::FromWebDatabase(db)->MaskServerCreditCard(id)) return WebDatabase::COMMIT_NEEDED; @@ -520,6 +525,15 @@ WebDatabase::State AutofillWebDataBackendImpl::UpdateServerAddressMetadata( return WebDatabase::COMMIT_NEEDED; } +WebDatabase::State AutofillWebDataBackendImpl::AddVPA(const std::string& vpa_id, + WebDatabase* db) { + DCHECK(owning_task_runner()->RunsTasksInCurrentSequence()); + + if (!AutofillTable::FromWebDatabase(db)->InsertVPA(vpa_id)) + return WebDatabase::COMMIT_NOT_NEEDED; + return WebDatabase::COMMIT_NEEDED; +} + std::unique_ptr<WDTypedResult> AutofillWebDataBackendImpl::GetPaymentsCustomerData(WebDatabase* db) { DCHECK(owning_task_runner()->RunsTasksInCurrentSequence()); @@ -550,10 +564,10 @@ WebDatabase::State AutofillWebDataBackendImpl::ClearAllLocalData( } WebDatabase::State - AutofillWebDataBackendImpl::RemoveAutofillDataModifiedBetween( - const base::Time& delete_begin, - const base::Time& delete_end, - WebDatabase* db) { +AutofillWebDataBackendImpl::RemoveAutofillDataModifiedBetween( + const base::Time& delete_begin, + const base::Time& delete_end, + WebDatabase* db) { DCHECK(owning_task_runner()->RunsTasksInCurrentSequence()); std::vector<std::unique_ptr<AutofillProfile>> profiles; std::vector<std::unique_ptr<CreditCard>> credit_cards; diff --git a/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h b/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h index 16a0151ff85..0dbb45d2404 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h +++ b/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h @@ -161,8 +161,7 @@ class AutofillWebDataBackendImpl WebDatabase* db); // Removes a credit card from the web database. Valid only for local cards. - WebDatabase::State RemoveCreditCard(const std::string& guid, - WebDatabase* db); + WebDatabase::State RemoveCreditCard(const std::string& guid, WebDatabase* db); // Adds a full server credit card to the web database. WebDatabase::State AddFullServerCreditCard(const CreditCard& credit_card, @@ -186,6 +185,8 @@ class AutofillWebDataBackendImpl WebDatabase::State UpdateServerAddressMetadata(const AutofillProfile& profile, WebDatabase* db); + WebDatabase::State AddVPA(const std::string& vpa_id, WebDatabase* db); + // Returns the PaymentsCustomerData from the database. std::unique_ptr<WDTypedResult> GetPaymentsCustomerData(WebDatabase* db); diff --git a/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.cc b/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.cc index 90f3917982b..6f9d4604f78 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.cc @@ -231,6 +231,11 @@ void AutofillWebDataService::MaskServerCreditCard(const std::string& id) { autofill_backend_, id)); } +void AutofillWebDataService::AddVPA(const std::string& vpa_id) { + wdbs_->ScheduleDBTask(FROM_HERE, Bind(&AutofillWebDataBackendImpl::AddVPA, + autofill_backend_, vpa_id)); +} + WebDataServiceBase::Handle AutofillWebDataService::GetPaymentsCustomerData( WebDataServiceConsumer* consumer) { return wdbs_->ScheduleDBTaskWithResult( diff --git a/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.h b/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.h index 43184406221..a12c516e8cf 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.h +++ b/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.h @@ -139,6 +139,9 @@ class AutofillWebDataService : public WebDataServiceBase { const base::string16& full_number); void MaskServerCreditCard(const std::string& id); + // Store a UPI/VPA value. + void AddVPA(const std::string& vpa_id); + // Initiates the request for Payments customer data. The method // OnWebDataServiceRequestDone of |consumer| gets called when the request is // finished, with the customer data included in the argument |result|. The diff --git a/chromium/components/autofill/core/browser/webdata/autofill_webdata_service_observer.h b/chromium/components/autofill/core/browser/webdata/autofill_webdata_service_observer.h index 7fd955e98cc..60820bb1a51 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_webdata_service_observer.h +++ b/chromium/components/autofill/core/browser/webdata/autofill_webdata_service_observer.h @@ -23,13 +23,6 @@ class AutofillWebDataServiceObserverOnDBSequence { // the WebDatabase. virtual void CreditCardChanged(const CreditCardChange& change) {} - // Called on DB sequence when multiple Autofill entries have been modified by - // Sync. - // TODO(crbug.com/900607): Remove AutofillMultipleChangedBySync() from - // AutofillWebDataServiceObserverOnDBSequence once USS for wallet_metadata - // launches. - virtual void AutofillMultipleChangedBySync() {} - protected: virtual ~AutofillWebDataServiceObserverOnDBSequence() {} }; diff --git a/chromium/components/autofill/core/browser/webdata/web_data_service_unittest.cc b/chromium/components/autofill/core/browser/webdata/web_data_service_unittest.cc index 8a3d73167f7..fbdec05751a 100644 --- a/chromium/components/autofill/core/browser/webdata/web_data_service_unittest.cc +++ b/chromium/components/autofill/core/browser/webdata/web_data_service_unittest.cc @@ -28,6 +28,7 @@ #include "components/autofill/core/browser/webdata/autofill_table.h" #include "components/autofill/core/browser/webdata/autofill_webdata_service.h" #include "components/autofill/core/browser/webdata/autofill_webdata_service_observer.h" +#include "components/autofill/core/common/autofill_clock.h" #include "components/autofill/core/common/form_field_data.h" #include "components/webdata/common/web_data_results.h" #include "components/webdata/common/web_data_service_base.h" @@ -48,7 +49,7 @@ using testing::ElementsAreArray; namespace { template <class T> -class AutofillWebDataServiceConsumer: public WebDataServiceConsumer { +class AutofillWebDataServiceConsumer : public WebDataServiceConsumer { public: AutofillWebDataServiceConsumer() : handle_(0) {} virtual ~AutofillWebDataServiceConsumer() {} @@ -82,8 +83,7 @@ ACTION_P(SignalEvent, event) { class MockAutofillWebDataServiceObserver : public AutofillWebDataServiceObserverOnDBSequence { public: - MOCK_METHOD1(AutofillEntriesChanged, - void(const AutofillChangeList& changes)); + MOCK_METHOD1(AutofillEntriesChanged, void(const AutofillChangeList& changes)); MOCK_METHOD1(AutofillProfileChanged, void(const AutofillProfileChange& change)); }; @@ -183,9 +183,8 @@ class WebDataServiceAutofillTest : public WebDataServiceTest { TEST_F(WebDataServiceAutofillTest, FormFillAdd) { const AutofillChange expected_changes[] = { - AutofillChange(AutofillChange::ADD, AutofillKey(name1_, value1_)), - AutofillChange(AutofillChange::ADD, AutofillKey(name2_, value2_)) - }; + AutofillChange(AutofillChange::ADD, AutofillKey(name1_, value1_)), + AutofillChange(AutofillChange::ADD, AutofillKey(name2_, value2_))}; // This will verify that the correct notification is triggered, // passing the correct list of autofill keys in the details. @@ -204,8 +203,8 @@ TEST_F(WebDataServiceAutofillTest, FormFillAdd) { AutofillWebDataServiceConsumer<std::vector<AutofillEntry>> consumer; WebDataServiceBase::Handle handle; static const int limit = 10; - handle = wds_->GetFormValuesForElementName( - name1_, base::string16(), limit, &consumer); + handle = wds_->GetFormValuesForElementName(name1_, base::string16(), limit, + &consumer); task_environment_.RunUntilIdle(); EXPECT_EQ(handle, consumer.handle()); ASSERT_EQ(1U, consumer.result().size()); @@ -226,8 +225,7 @@ TEST_F(WebDataServiceAutofillTest, FormFillRemoveOne) { // This will verify that the correct notification is triggered, // passing the correct list of autofill keys in the details. const AutofillChange expected_changes[] = { - AutofillChange(AutofillChange::REMOVE, AutofillKey(name1_, value1_)) - }; + AutofillChange(AutofillChange::REMOVE, AutofillKey(name1_, value1_))}; EXPECT_CALL(observer_, AutofillEntriesChanged(ElementsAreArray(expected_changes))) .WillOnce(SignalEvent(&done_event_)); @@ -239,7 +237,7 @@ TEST_F(WebDataServiceAutofillTest, FormFillRemoveOne) { TEST_F(WebDataServiceAutofillTest, FormFillRemoveMany) { TimeDelta one_day(TimeDelta::FromDays(1)); - Time t = Time::Now(); + Time t = AutofillClock::Now(); EXPECT_CALL(observer_, AutofillEntriesChanged(_)) .WillOnce(SignalEvent(&done_event_)); @@ -255,9 +253,8 @@ TEST_F(WebDataServiceAutofillTest, FormFillRemoveMany) { // This will verify that the correct notification is triggered, // passing the correct list of autofill keys in the details. const AutofillChange expected_changes[] = { - AutofillChange(AutofillChange::REMOVE, AutofillKey(name1_, value1_)), - AutofillChange(AutofillChange::REMOVE, AutofillKey(name2_, value2_)) - }; + AutofillChange(AutofillChange::REMOVE, AutofillKey(name1_, value1_)), + AutofillChange(AutofillChange::REMOVE, AutofillKey(name2_, value2_))}; EXPECT_CALL(observer_, AutofillEntriesChanged(ElementsAreArray(expected_changes))) .WillOnce(SignalEvent(&done_event_)); @@ -271,8 +268,8 @@ TEST_F(WebDataServiceAutofillTest, ProfileAdd) { AutofillProfile profile; // Check that GUID-based notification was sent. - const AutofillProfileChange expected_change( - AutofillProfileChange::ADD, profile.guid(), &profile); + const AutofillProfileChange expected_change(AutofillProfileChange::ADD, + profile.guid(), &profile); EXPECT_CALL(observer_, AutofillProfileChanged(expected_change)) .WillOnce(SignalEvent(&done_event_)); diff --git a/chromium/components/autofill/core/common/BUILD.gn b/chromium/components/autofill/core/common/BUILD.gn index 0bb58c919d6..2f096cab5d5 100644 --- a/chromium/components/autofill/core/common/BUILD.gn +++ b/chromium/components/autofill/core/common/BUILD.gn @@ -29,6 +29,8 @@ jumbo_static_library("common") { "autofill_regexes.h", "autofill_switches.cc", "autofill_switches.h", + "autofill_tick_clock.cc", + "autofill_tick_clock.h", "autofill_util.cc", "autofill_util.h", "form_data.cc", @@ -39,9 +41,10 @@ jumbo_static_library("common") { "form_field_data.h", "form_field_data_predictions.cc", "form_field_data_predictions.h", + "logging/log_buffer.cc", + "logging/log_buffer.h", "password_form.cc", "password_form.h", - "password_form_field_prediction_map.h", "password_form_fill_data.cc", "password_form_fill_data.h", "password_form_generation_data.cc", @@ -75,12 +78,15 @@ jumbo_static_library("common") { source_set("unit_tests") { testonly = true sources = [ + "autofill_internals/log_message_unittest.cc", + "autofill_internals/logging_scope_unittest.cc", "autofill_l10n_util_unittest.cc", "autofill_prefs_unittest.cc", "autofill_regexes_unittest.cc", "autofill_util_unittest.cc", "form_data_unittest.cc", "form_field_data_unittest.cc", + "logging/log_buffer_unittest.cc", "password_form_fill_data_unittest.cc", "save_password_progress_logger_unittest.cc", ] diff --git a/chromium/components/autofill/core/common/autofill_clock.cc b/chromium/components/autofill/core/common/autofill_clock.cc index 0336eb95132..60169c8c7fb 100644 --- a/chromium/components/autofill/core/common/autofill_clock.cc +++ b/chromium/components/autofill/core/common/autofill_clock.cc @@ -9,7 +9,7 @@ namespace autofill { namespace { -base::Clock* g_autofill_clock = nullptr; +const base::Clock* g_autofill_clock = nullptr; } // namespace // static @@ -25,7 +25,7 @@ void AutofillClock::SetClock() { } // static -void AutofillClock::SetTestClock(base::Clock* clock) { +void AutofillClock::SetTestClock(const base::Clock* clock) { DCHECK(clock); g_autofill_clock = clock; } diff --git a/chromium/components/autofill/core/common/autofill_clock.h b/chromium/components/autofill/core/common/autofill_clock.h index 651d9cdf496..2280d16851c 100644 --- a/chromium/components/autofill/core/common/autofill_clock.h +++ b/chromium/components/autofill/core/common/autofill_clock.h @@ -16,7 +16,7 @@ namespace autofill { // with a customizable clock to facilitate testing. class AutofillClock { public: - // Returns the current time based on |clock_|. + // Returns the current time based last set clock. static base::Time Now(); private: @@ -26,7 +26,7 @@ class AutofillClock { static void SetClock(); // Sets the clock to be used for tests. - static void SetTestClock(base::Clock* clock); + static void SetTestClock(const base::Clock* clock); AutofillClock() = delete; ~AutofillClock() = delete; diff --git a/chromium/components/autofill/core/common/autofill_features.cc b/chromium/components/autofill/core/common/autofill_features.cc index d7a0b6e4957..7f4ca82a5bf 100644 --- a/chromium/components/autofill/core/common/autofill_features.cc +++ b/chromium/components/autofill/core/common/autofill_features.cc @@ -112,6 +112,11 @@ const base::Feature kAutofillProfileServerValidation{ const base::Feature kAutofillRejectCompanyBirthyear{ "AutofillRejectCompanyBirthyear", base::FEATURE_DISABLED_BY_DEFAULT}; +// Controls whether autofill rejects using non-verified company names that are +// social titles (e.g., "Mrs.") in some languages. +const base::Feature kAutofillRejectCompanySocialTitle{ + "AutofillRejectCompanySocialTitle", base::FEATURE_DISABLED_BY_DEFAULT}; + // Controls whether or not a group of fields not enclosed in a form can be // considered a form. If this is enabled, unowned fields will only constitute // a form if there are signals to suggest that this might a checkout page. @@ -126,6 +131,10 @@ const base::Feature kAutofillRestrictUnownedFieldsToFormlessCheckout{ const base::Feature kAutofillRichMetadataQueries{ "AutofillRichMetadataQueries", base::FEATURE_DISABLED_BY_DEFAULT}; +// Controls whether UPI/VPA values will be saved and filled into payment forms. +const base::Feature kAutofillSaveAndFillVPA{"AutofillSaveAndFillVPA", + base::FEATURE_DISABLED_BY_DEFAULT}; + const base::Feature kAutofillSaveOnProbablySubmitted{ "AutofillSaveOnProbablySubmitted", base::FEATURE_ENABLED_BY_DEFAULT}; @@ -165,6 +174,10 @@ const base::Feature kAutofillSkipComparingInferredLabels{ const base::Feature kAutofillTokenPrefixMatching{ "AutofillTokenPrefixMatching", base::FEATURE_DISABLED_BY_DEFAULT}; +// Enables the touch to fill feature for Android. +const base::Feature kAutofillTouchToFill = {"TouchToFillAndroid", + base::FEATURE_DISABLED_BY_DEFAULT}; + const base::Feature kAutofillUploadThrottling{"AutofillUploadThrottling", base::FEATURE_ENABLED_BY_DEFAULT}; @@ -178,6 +191,10 @@ const base::Feature kAutofillUseImprovedLabelDisambiguation{ "AutofillUseImprovedLabelDisambiguation", base::FEATURE_DISABLED_BY_DEFAULT}; +// Server predictions for CVC fields are used if the feature is enabled. +const base::Feature kAutofillUseServerCVCPrediction{ + "AutofillUseServerCVCPrediction", base::FEATURE_ENABLED_BY_DEFAULT}; + #if defined(OS_ANDROID) // Controls whether the Autofill manual fallback for Addresses and Payments is // present on Android. @@ -188,10 +205,6 @@ const base::Feature kAutofillManualFallbackAndroid{ const base::Feature kAutofillRefreshStyleAndroid{ "AutofillRefreshStyleAndroid", base::FEATURE_DISABLED_BY_DEFAULT}; -// Enables the touch to fill feature for Android. -const base::Feature kTouchToFillAndroid = {"TouchToFillAndroid", - base::FEATURE_DISABLED_BY_DEFAULT}; - #endif // OS_ANDROID #if defined(OS_ANDROID) || defined(OS_IOS) diff --git a/chromium/components/autofill/core/common/autofill_features.h b/chromium/components/autofill/core/common/autofill_features.h index 8e6937f5447..78a8bfcbbbc 100644 --- a/chromium/components/autofill/core/common/autofill_features.h +++ b/chromium/components/autofill/core/common/autofill_features.h @@ -42,8 +42,10 @@ extern const base::Feature kAutofillPreferServerNamePredictions; extern const base::Feature kAutofillProfileClientValidation; extern const base::Feature kAutofillProfileServerValidation; extern const base::Feature kAutofillRejectCompanyBirthyear; +extern const base::Feature kAutofillRejectCompanySocialTitle; extern const base::Feature kAutofillRestrictUnownedFieldsToFormlessCheckout; extern const base::Feature kAutofillRichMetadataQueries; +extern const base::Feature kAutofillSaveAndFillVPA; extern const base::Feature kAutofillSaveOnProbablySubmitted; extern const base::Feature kAutofillServerCommunication; extern const base::Feature kAutofillShowAllSuggestionsOnPrefilledForms; @@ -51,14 +53,14 @@ extern const base::Feature kAutofillShowAutocompleteConsoleWarnings; extern const base::Feature kAutofillShowTypePredictions; extern const base::Feature kAutofillSkipComparingInferredLabels; extern const base::Feature kAutofillTokenPrefixMatching; +extern const base::Feature kAutofillTouchToFill; extern const base::Feature kAutofillUploadThrottling; extern const base::Feature kAutofillUseApi; extern const base::Feature kAutofillUseImprovedLabelDisambiguation; - +extern const base::Feature kAutofillUseServerCVCPrediction; #if defined(OS_ANDROID) extern const base::Feature kAutofillManualFallbackAndroid; extern const base::Feature kAutofillRefreshStyleAndroid; -extern const base::Feature kTouchToFillAndroid; #endif // OS_ANDROID #if defined(OS_ANDROID) || defined(OS_IOS) diff --git a/chromium/components/autofill/core/common/autofill_internals/log_message.cc b/chromium/components/autofill/core/common/autofill_internals/log_message.cc index 450258c661c..60da13de84e 100644 --- a/chromium/components/autofill/core/common/autofill_internals/log_message.cc +++ b/chromium/components/autofill/core/common/autofill_internals/log_message.cc @@ -5,6 +5,7 @@ #include "components/autofill/core/common/autofill_internals/log_message.h" #include "base/logging.h" +#include "components/autofill/core/common/logging/log_buffer.h" namespace autofill { @@ -50,4 +51,11 @@ const char* LogMessageValue(LogMessage message) { return ""; } +LogBuffer& operator<<(LogBuffer& buf, LogMessage message) { + if (!buf.active()) + return buf; + return buf << Tag{"div"} << Attrib{"message", LogMessageToString(message)} + << Attrib{"class", "log-message"} << LogMessageValue(message); +} + } // namespace autofill diff --git a/chromium/components/autofill/core/common/autofill_internals/log_message.h b/chromium/components/autofill/core/common/autofill_internals/log_message.h index 4862e5e448e..a7232ce6cbb 100644 --- a/chromium/components/autofill/core/common/autofill_internals/log_message.h +++ b/chromium/components/autofill/core/common/autofill_internals/log_message.h @@ -7,15 +7,28 @@ namespace autofill { +class LogBuffer; + /////////////// Log Messages ///////////// // Generator for log message. If you need to find the call site for a log // message, take the first parameter (e.g. ParsedForms) and search for // that name prefixed with a k (e.g. kParsedForms) in code search. -#define AUTOFILL_LOG_MESSAGE_TEMPLATES(T) \ - T(ParsedForms, "Parsed forms:") \ - T(SendAutofillUpload, "Sending Autofill Upload Request:") \ - T(LocalHeuristicRegExMatched, "RegEx of local heuristic matched:") +#define AUTOFILL_LOG_MESSAGE_TEMPLATES(T) \ + T(ParsedForms, "Parsed forms:") \ + T(SendAutofillUpload, "Sending Autofill Upload Request:") \ + T(LocalHeuristicRegExMatched, "RegEx of local heuristic matched:") \ + T(AbortParsingTooManyForms, "Abort parsing form: Too many forms in cache: ") \ + T(AbortParsingNotAllowedScheme, \ + "Abort parsing form: Ignoring form because the source url has no allowed " \ + "scheme") \ + T(AbortParsingNotEnoughFields, \ + "Abort parsing form: Not enough fields in form: ") \ + T(AbortParsingUrlMatchesSearchRegex, \ + "Abort parsing form: Action URL matches kUrlSearchActionRe, indicating " \ + "that the form may lead to a search.") \ + T(AbortParsingFormHasNoTextfield, \ + "Abort parsing form: Form has no text field.") // Log messages for chrome://autofill-internals. #define AUTOFILL_TEMPLATE(NAME, MESSAGE) k##NAME, @@ -29,6 +42,8 @@ const char* LogMessageToString(LogMessage message); // Returns the actual string to be presented to the user for |message|. const char* LogMessageValue(LogMessage message); +LogBuffer& operator<<(LogBuffer& buf, LogMessage message); + } // namespace autofill #endif // COMPONENTS_AUTOFILL_CORE_COMMON_AUTOFILL_INTERNALS_LOG_MESSAGE_H_ diff --git a/chromium/components/autofill/core/browser/autofill_internals_service_unittest.cc b/chromium/components/autofill/core/common/autofill_internals/log_message_unittest.cc index 2baaee02348..2d4f225cb99 100644 --- a/chromium/components/autofill/core/browser/autofill_internals_service_unittest.cc +++ b/chromium/components/autofill/core/common/autofill_internals/log_message_unittest.cc @@ -2,26 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "components/autofill/core/browser/autofill_internals_service.h" +#include "components/autofill/core/common/autofill_internals/log_message.h" #include "base/json/json_writer.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest-death-test.h" +#include "components/autofill/core/common/logging/log_buffer.h" #include "testing/gtest/include/gtest/gtest.h" namespace autofill { -TEST(AutofillInternalsService, Scope) { - LogBuffer buffer; - buffer << LoggingScope::kContext; - std::string json; - EXPECT_TRUE(base::JSONWriter::Write(buffer.RetrieveResult(), &json)); - EXPECT_EQ(R"({"attributes":{"class":"log-entry","scope":"Context"},)" - R"("type":"element","value":"div"})", - json); -} - -TEST(AutofillInternalsService, Message) { +TEST(LogMessage, Serialization) { LogBuffer buffer; buffer << LogMessage::kParsedForms; std::string json; diff --git a/chromium/components/autofill/core/common/autofill_internals/logging_scope.cc b/chromium/components/autofill/core/common/autofill_internals/logging_scope.cc index ab962d5eaf3..2e1597b07bf 100644 --- a/chromium/components/autofill/core/common/autofill_internals/logging_scope.cc +++ b/chromium/components/autofill/core/common/autofill_internals/logging_scope.cc @@ -5,6 +5,7 @@ #include "components/autofill/core/common/autofill_internals/logging_scope.h" #include "base/logging.h" +#include "components/autofill/core/common/logging/log_buffer.h" namespace autofill { @@ -29,4 +30,11 @@ const char* LoggingScopeToString(LoggingScope scope) { return ""; } +LogBuffer& operator<<(LogBuffer& buf, LoggingScope scope) { + if (!buf.active()) + return buf; + return buf << Tag{"div"} << Attrib{"scope", LoggingScopeToString(scope)} + << Attrib{"class", "log-entry"}; +} + } // namespace autofill diff --git a/chromium/components/autofill/core/common/autofill_internals/logging_scope.h b/chromium/components/autofill/core/common/autofill_internals/logging_scope.h index ac719ca73dd..5684511f84e 100644 --- a/chromium/components/autofill/core/common/autofill_internals/logging_scope.h +++ b/chromium/components/autofill/core/common/autofill_internals/logging_scope.h @@ -7,6 +7,8 @@ namespace autofill { +class LogBuffer; + /////////////// Logging Scopes ///////////// // Generator for source code related to logging scopes. Pass a template T which @@ -16,6 +18,8 @@ namespace autofill { T(Context) \ /* Log messages related to the discovery and parsing of forms. */ \ T(Parsing) \ + /* Log messages related to reasons to stop parsing a form. */ \ + T(AbortParsing) \ /* Log messages related to filling of forms. */ \ T(Filling) \ /* Log messages related to the submission of forms. */ \ @@ -33,6 +37,8 @@ enum class LoggingScope { // Returns the enum value of |scope| as a string (without the leading k). const char* LoggingScopeToString(LoggingScope scope); +LogBuffer& operator<<(LogBuffer& buf, LoggingScope scope); + } // namespace autofill #endif // COMPONENTS_AUTOFILL_CORE_COMMON_AUTOFILL_INTERNALS_LOGGING_SCOPE_H_ diff --git a/chromium/components/autofill/core/common/autofill_internals/logging_scope_unittest.cc b/chromium/components/autofill/core/common/autofill_internals/logging_scope_unittest.cc new file mode 100644 index 00000000000..16ffe96f22c --- /dev/null +++ b/chromium/components/autofill/core/common/autofill_internals/logging_scope_unittest.cc @@ -0,0 +1,23 @@ +// Copyright 2019 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 "components/autofill/core/common/autofill_internals/logging_scope.h" + +#include "base/json/json_writer.h" +#include "components/autofill/core/common/logging/log_buffer.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace autofill { + +TEST(LoggingScope, Serialization) { + LogBuffer buffer; + buffer << LoggingScope::kContext; + std::string json; + EXPECT_TRUE(base::JSONWriter::Write(buffer.RetrieveResult(), &json)); + EXPECT_EQ(R"({"attributes":{"class":"log-entry","scope":"Context"},)" + R"("type":"element","value":"div"})", + json); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/common/autofill_payments_features.cc b/chromium/components/autofill/core/common/autofill_payments_features.cc index 6fd64901fe7..d5a33bdaa85 100644 --- a/chromium/components/autofill/core/common/autofill_payments_features.cc +++ b/chromium/components/autofill/core/common/autofill_payments_features.cc @@ -34,6 +34,14 @@ const char kAutofillSaveCreditCardUsesImprovedMessagingParamValueConfirmAndSaveCard[] = "Confirm & Save Card"; +// Features + +// Controls whether or not Autofill client will populate form with CPAN and +// dCVV, rather than FPAN. +const base::Feature kAutofillAlwaysReturnCloudTokenizedCard{ + "AutofillAlwaysReturnCloudTokenizedCard", + base::FEATURE_DISABLED_BY_DEFAULT}; + const base::Feature kAutofillCreditCardAblationExperiment{ "AutofillCreditCardAblationExperiment", base::FEATURE_DISABLED_BY_DEFAULT}; @@ -52,12 +60,6 @@ const base::Feature kAutofillDoNotMigrateUnsupportedLocalCards{ "AutofillDoNotMigrateUnsupportedLocalCards", base::FEATURE_ENABLED_BY_DEFAULT}; -// Controls whether the credit card downstream keyboard accessory shows -// the Google Pay logo animation on iOS. -const base::Feature kAutofillDownstreamUseGooglePayBrandingOniOS{ - "AutofillDownstreamUseGooglePayBrandingOniOS", - base::FEATURE_DISABLED_BY_DEFAULT}; - // When enabled, enable local card migration flow for user who has signed in but // has not turned on sync. const base::Feature kAutofillEnableLocalCardMigrationForNonSyncUser{ @@ -69,15 +71,11 @@ const base::Feature kAutofillEnableLocalCardMigrationForNonSyncUser{ const base::Feature kAutofillEnableToolbarStatusChip{ "AutofillEnableToolbarStatusChip", base::FEATURE_DISABLED_BY_DEFAULT}; -// When enabled, autofill can import credit cards from dynamic change form. -const base::Feature kAutofillImportDynamicForms{ - "AutofillImportDynamicForms", base::FEATURE_ENABLED_BY_DEFAULT}; - // Controls whether offering to migrate cards will consider data from the // Autofill strike database (new version). const base::Feature kAutofillLocalCardMigrationUsesStrikeSystemV2{ "AutofillLocalCardMigrationUsesStrikeSystemV2", - base::FEATURE_DISABLED_BY_DEFAULT}; + base::FEATURE_ENABLED_BY_DEFAULT}; // When enabled, will remove the option to save unmasked server cards as // FULL_SERVER_CARDs upon successful unmask. @@ -89,12 +87,17 @@ const base::Feature kAutofillNoLocalSaveOnUnmaskSuccess{ const base::Feature kAutofillNoLocalSaveOnUploadSuccess{ "AutofillNoLocalSaveOnUploadSuccess", base::FEATURE_DISABLED_BY_DEFAULT}; +// When enabled, the Save Card infobar will be dismissed by a user initiated +// navigation other than one caused by submitted form. +const base::Feature kAutofillSaveCardDismissOnNavigation{ + "AutofillSaveCardDismissOnNavigation", base::FEATURE_ENABLED_BY_DEFAULT}; + // When enabled, local and upload credit card save dialogs will add a // [No thanks] cancel button option. This is intended to bring the // AutofillSaveCardImprovedUserConsent functionality to Chrome OS, Android, and // iOS without bringing the extended title string change with it. const base::Feature kAutofillSaveCardShowNoThanks{ - "AutofillSaveCardShowNoThanks", base::FEATURE_DISABLED_BY_DEFAULT}; + "AutofillSaveCardShowNoThanks", base::FEATURE_ENABLED_BY_DEFAULT}; // Controls what title and bubble label for the credit card upload bubble are // shown to users. diff --git a/chromium/components/autofill/core/common/autofill_payments_features.h b/chromium/components/autofill/core/common/autofill_payments_features.h index b0f0e2827e5..6dc8e2faa2d 100644 --- a/chromium/components/autofill/core/common/autofill_payments_features.h +++ b/chromium/components/autofill/core/common/autofill_payments_features.h @@ -19,17 +19,17 @@ namespace autofill { namespace features { // All features in alphabetical order. +extern const base::Feature kAutofillAlwaysReturnCloudTokenizedCard; extern const base::Feature kAutofillCreditCardAblationExperiment; extern const base::Feature kAutofillCreditCardAuthentication; extern const base::Feature kAutofillCreditCardUploadFeedback; extern const base::Feature kAutofillDoNotMigrateUnsupportedLocalCards; -extern const base::Feature kAutofillDownstreamUseGooglePayBrandingOniOS; extern const base::Feature kAutofillEnableLocalCardMigrationForNonSyncUser; extern const base::Feature kAutofillEnableToolbarStatusChip; -extern const base::Feature kAutofillImportDynamicForms; extern const base::Feature kAutofillLocalCardMigrationUsesStrikeSystemV2; extern const base::Feature kAutofillNoLocalSaveOnUnmaskSuccess; extern const base::Feature kAutofillNoLocalSaveOnUploadSuccess; +extern const base::Feature kAutofillSaveCardDismissOnNavigation; extern const base::Feature kAutofillSaveCardShowNoThanks; extern const base::Feature kAutofillSaveCreditCardUsesImprovedMessaging; extern const base::Feature kAutofillUpdatedCardUnmaskPromptUi; diff --git a/chromium/components/autofill/core/common/autofill_prefs.cc b/chromium/components/autofill/core/common/autofill_prefs.cc index 7801f45ada6..1607b0d7274 100644 --- a/chromium/components/autofill/core/common/autofill_prefs.cc +++ b/chromium/components/autofill/core/common/autofill_prefs.cc @@ -18,7 +18,6 @@ namespace { // was found. int GetSyncTransportOptInBitFieldForAccount(const PrefService* prefs, const std::string& account_hash) { - auto* dictionary = prefs->GetDictionary(prefs::kAutofillSyncTransportOptIn); // If there is no dictionary it means the account didn't opt-in. Use 0 because @@ -43,14 +42,18 @@ int GetSyncTransportOptInBitFieldForAccount(const PrefService* prefs, const char kAutofillAcceptSaveCreditCardPromptState[] = "autofill.accept_save_credit_card_prompt_state"; -// Boolean that is true if FIDO Authentication is enabled for card unmasking. -const char kAutofillCreditCardFIDOAuthEnabled[] = - "autofill.credit_card_fido_auth_enabled"; - // Boolean that is true if Autofill is enabled and allowed to save credit card // data. const char kAutofillCreditCardEnabled[] = "autofill.credit_card_enabled"; +// Boolean that is true if FIDO Authentication is enabled for card unmasking. +const char kAutofillCreditCardFidoAuthEnabled[] = + "autofill.credit_card_fido_auth_enabled"; + +// Boolean that is true if FIDO Authentication is enabled for card unmasking. +const char kAutofillCreditCardFidoAuthOfferCheckboxState[] = + "autofill.credit_card_fido_auth_offer_checkbox_state"; + // Number of times the credit card signin promo has been shown. const char kAutofillCreditCardSigninPromoImpressionCount[] = "autofill.credit_card_signin_promo_impression_count"; @@ -58,9 +61,8 @@ const char kAutofillCreditCardSigninPromoImpressionCount[] = // Boolean that is true if Autofill is enabled and allowed to save data. const char kAutofillEnabledDeprecated[] = "autofill.enabled"; -// Boolean that is true if Japan address city field has been migrated to be a -// part of the street field. -const char kAutofillJapanCityFieldMigrated[] = +// Deprecated 10/2019. +const char kAutofillJapanCityFieldMigratedDeprecated[] = "autofill.japan_city_field_migrated_to_street_address"; // Integer that is set to the last version where the profile deduping routine @@ -157,11 +159,12 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) { user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF); // Non-synced prefs. Used for per-device choices, e.g., signin promo. - registry->RegisterBooleanPref(prefs::kAutofillCreditCardFIDOAuthEnabled, + registry->RegisterBooleanPref(prefs::kAutofillCreditCardFidoAuthEnabled, false); + registry->RegisterBooleanPref( + prefs::kAutofillCreditCardFidoAuthOfferCheckboxState, true); registry->RegisterIntegerPref( prefs::kAutofillCreditCardSigninPromoImpressionCount, 0); - registry->RegisterBooleanPref(prefs::kAutofillJapanCityFieldMigrated, false); registry->RegisterBooleanPref(prefs::kAutofillWalletImportEnabled, true); registry->RegisterBooleanPref( prefs::kAutofillWalletImportStorageCheckboxState, true); @@ -180,6 +183,10 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) { registry->RegisterTimePref(prefs::kAutofillUploadEventsLastResetTimestamp, base::Time()); registry->RegisterDictionaryPref(prefs::kAutofillSyncTransportOptIn); + + // Deprecated prefs registered for migration. + registry->RegisterBooleanPref(kAutofillJapanCityFieldMigratedDeprecated, + false); } void MigrateDeprecatedAutofillPrefs(PrefService* prefs) { @@ -210,6 +217,9 @@ void MigrateDeprecatedAutofillPrefs(PrefService* prefs) { prefs->SetBoolean(kAutofillProfileEnabled, prefs->GetBoolean(kAutofillEnabledDeprecated)); } + + // Added 10/2019. + prefs->ClearPref(kAutofillJapanCityFieldMigratedDeprecated); } bool IsAutocompleteEnabled(const PrefService* prefs) { @@ -226,11 +236,11 @@ void SetAutofillEnabled(PrefService* prefs, bool enabled) { } bool IsCreditCardFIDOAuthEnabled(PrefService* prefs) { - return prefs->GetBoolean(kAutofillCreditCardFIDOAuthEnabled); + return prefs->GetBoolean(kAutofillCreditCardFidoAuthEnabled); } void SetCreditCardFIDOAuthEnabled(PrefService* prefs, bool enabled) { - prefs->SetBoolean(kAutofillCreditCardFIDOAuthEnabled, enabled); + prefs->SetBoolean(kAutofillCreditCardFidoAuthEnabled, enabled); } bool IsCreditCardAutofillEnabled(const PrefService* prefs) { diff --git a/chromium/components/autofill/core/common/autofill_prefs.h b/chromium/components/autofill/core/common/autofill_prefs.h index f807434285e..7558441a2ac 100644 --- a/chromium/components/autofill/core/common/autofill_prefs.h +++ b/chromium/components/autofill/core/common/autofill_prefs.h @@ -20,12 +20,13 @@ namespace prefs { // component. Keep alphabetized, and document each in the .cc file. extern const char kAutofillAcceptSaveCreditCardPromptState[]; // Do not get/set the value of this pref directly. Use provided getter/setter. -extern const char kAutofillCreditCardFIDOAuthEnabled[]; extern const char kAutofillCreditCardEnabled[]; +extern const char kAutofillCreditCardFidoAuthEnabled[]; +extern const char kAutofillCreditCardFidoAuthOfferCheckboxState[]; extern const char kAutofillCreditCardSigninPromoImpressionCount[]; // Please use kAutofillCreditCardEnabled and kAutofillProfileEnabled instead. extern const char kAutofillEnabledDeprecated[]; -extern const char kAutofillJapanCityFieldMigrated[]; +extern const char kAutofillJapanCityFieldMigratedDeprecated[]; extern const char kAutofillLastVersionDeduped[]; extern const char kAutofillLastVersionValidated[]; extern const char kAutofillLastVersionDisusedAddressesDeleted[]; diff --git a/chromium/components/autofill/core/common/autofill_regex_constants.cc b/chromium/components/autofill/core/common/autofill_regex_constants.cc index 7cb75718ae4..daacd27d66a 100644 --- a/chromium/components/autofill/core/common/autofill_regex_constants.cc +++ b/chromium/components/autofill/core/common/autofill_regex_constants.cc @@ -176,13 +176,14 @@ const char kNameOnCardRe[] = const char kNameOnCardContextualRe[] = "name"; const char kCardNumberRe[] = "(add)?(?:card|cc|acct).?(?:number|#|no|num|field)" - "|(?<!telefon|haus)nummer" // de-DE - "|カード番号" // ja-JP - "|Номер.*карты" // ru - "|信用卡号|信用卡号码" // zh-CN - "|信用卡卡號" // zh-TW - "|카드" // ko-KR - "|(numero|número|numéro)(?!.*(document|fono|phone))"; // es/pt/fr + "|(?<!telefon|haus)nummer" // de-DE + "|カード番号" // ja-JP + "|Номер.*карты" // ru + "|信用卡号|信用卡号码" // zh-CN + "|信用卡卡號" // zh-TW + "|카드" // ko-KR + // es/pt/fr + "|(numero|número|numéro)(?!.*(document|fono|phone|réservation))"; const char kCardCvcRe[] = "verification|card.?identification|security.?code|card.?code" diff --git a/chromium/components/autofill/core/common/autofill_tick_clock.cc b/chromium/components/autofill/core/common/autofill_tick_clock.cc new file mode 100644 index 00000000000..594916e5c6f --- /dev/null +++ b/chromium/components/autofill/core/common/autofill_tick_clock.cc @@ -0,0 +1,33 @@ +// Copyright 2019 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 "components/autofill/core/common/autofill_tick_clock.h" + +#include "base/time/default_tick_clock.h" +#include "base/time/tick_clock.h" + +namespace autofill { +namespace { +const base::TickClock* g_autofill_tick_clock = nullptr; +} // namespace + +// static +base::TimeTicks AutofillTickClock::NowTicks() { + if (!g_autofill_tick_clock) + SetTickClock(); + return g_autofill_tick_clock->NowTicks(); +} + +// static +void AutofillTickClock::SetTickClock() { + g_autofill_tick_clock = base::DefaultTickClock::GetInstance(); +} + +// static +void AutofillTickClock::SetTestTickClock(const base::TickClock* tick_clock) { + DCHECK(tick_clock); + g_autofill_tick_clock = tick_clock; +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/common/autofill_tick_clock.h b/chromium/components/autofill/core/common/autofill_tick_clock.h new file mode 100644 index 00000000000..a438604ffed --- /dev/null +++ b/chromium/components/autofill/core/common/autofill_tick_clock.h @@ -0,0 +1,37 @@ +// Copyright 2019 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. + +#ifndef COMPONENTS_AUTOFILL_CORE_COMMON_AUTOFILL_TICK_CLOCK_H_ +#define COMPONENTS_AUTOFILL_CORE_COMMON_AUTOFILL_TICK_CLOCK_H_ + +namespace base { +class TickClock; +class TimeTicks; +} // namespace base + +namespace autofill { + +// Handles getting the current time for the Autofill feature. Can be injected +// with a customizable tick clock to facilitate testing. +class AutofillTickClock { + public: + // Returns the current time based last set tick clock. + static base::TimeTicks NowTicks(); + + private: + friend class TestAutofillTickClock; + + // Resets a normal tick clock. + static void SetTickClock(); + + // Sets the tick clock to be used for tests. + static void SetTestTickClock(const base::TickClock* tick_clock); + + AutofillTickClock() = delete; + ~AutofillTickClock() = delete; +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_COMMON_AUTOFILL_TICK_CLOCK_H_ diff --git a/chromium/components/autofill/core/common/autofill_util.cc b/chromium/components/autofill/core/common/autofill_util.cc index 19ec7815437..36c3a17d912 100644 --- a/chromium/components/autofill/core/common/autofill_util.cc +++ b/chromium/components/autofill/core/common/autofill_util.cc @@ -72,7 +72,7 @@ bool IsKeyboardAccessoryEnabled() { bool IsTouchToFillEnabled() { #if defined(OS_ANDROID) - return base::FeatureList::IsEnabled(features::kTouchToFillAndroid); + return base::FeatureList::IsEnabled(features::kAutofillTouchToFill); #else // !defined(OS_ANDROID) return false; #endif diff --git a/chromium/components/autofill/core/common/form_data.cc b/chromium/components/autofill/core/common/form_data.cc index 5f2f9c95122..e69ae3a8a28 100644 --- a/chromium/components/autofill/core/common/form_data.cc +++ b/chromium/components/autofill/core/common/form_data.cc @@ -12,6 +12,7 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/common/form_field_data.h" +#include "components/autofill/core/common/logging/log_buffer.h" namespace autofill { @@ -68,29 +69,22 @@ void LogDeserializationError(int version) { } // namespace -FormData::FormData() : is_form_tag(true), is_formless_checkout(false) {} - -FormData::FormData(const FormData& data) - : name(data.name), - button_titles(data.button_titles), - url(data.url), - action(data.action), - main_frame_origin(data.main_frame_origin), - is_form_tag(data.is_form_tag), - is_formless_checkout(data.is_formless_checkout), - unique_renderer_id(data.unique_renderer_id), - submission_event(data.submission_event), - fields(data.fields), - username_predictions(data.username_predictions), - is_gaia_with_skip_save_password_form( - data.is_gaia_with_skip_save_password_form) {} - -FormData::~FormData() { -} +FormData::FormData() = default; + +FormData::FormData(const FormData&) = default; + +FormData& FormData::operator=(const FormData&) = default; + +FormData::FormData(FormData&&) = default; + +FormData& FormData::operator=(FormData&&) = default; + +FormData::~FormData() = default; bool FormData::SameFormAs(const FormData& form) const { - if (name != form.name || url != form.url || action != form.action || - is_form_tag != form.is_form_tag || + if (name != form.name || id_attribute != form.id_attribute || + name_attribute != form.name_attribute || url != form.url || + action != form.action || is_form_tag != form.is_form_tag || is_formless_checkout != form.is_formless_checkout || fields.size() != form.fields.size()) return false; @@ -102,11 +96,14 @@ bool FormData::SameFormAs(const FormData& form) const { } bool FormData::SimilarFormAs(const FormData& form) const { - if (name != form.name || url != form.url || action != form.action || + if (name != form.name || id_attribute != form.id_attribute || + name_attribute != form.name_attribute || url != form.url || + action != form.action || is_action_empty != form.is_action_empty || is_form_tag != form.is_form_tag || is_formless_checkout != form.is_formless_checkout || - fields.size() != form.fields.size()) + fields.size() != form.fields.size()) { return false; + } for (size_t i = 0; i < fields.size(); ++i) { if (!fields[i].SimilarFieldAs(form.fields[i])) return false; @@ -115,7 +112,9 @@ bool FormData::SimilarFormAs(const FormData& form) const { } bool FormData::DynamicallySameFormAs(const FormData& form) const { - if (name != form.name || fields.size() != form.fields.size()) + if (name != form.name || id_attribute != form.id_attribute || + name_attribute != form.name_attribute || + fields.size() != form.fields.size()) return false; for (size_t i = 0; i < fields.size(); ++i) { if (!fields[i].DynamicallySameFieldAs(form.fields[i])) @@ -125,7 +124,9 @@ bool FormData::DynamicallySameFormAs(const FormData& form) const { } bool FormData::operator==(const FormData& form) const { - return name == form.name && url == form.url && action == form.action && + return name == form.name && id_attribute == form.id_attribute && + name_attribute == form.name_attribute && url == form.url && + action == form.action && is_action_empty == form.is_action_empty && unique_renderer_id == form.unique_renderer_id && submission_event == form.submission_event && is_form_tag == form.is_form_tag && @@ -139,10 +140,11 @@ bool FormData::operator!=(const FormData& form) const { } bool FormData::operator<(const FormData& form) const { - return std::tie(name, url, action, is_form_tag, is_formless_checkout, - fields) < std::tie(form.name, form.url, form.action, - form.is_form_tag, - form.is_formless_checkout, form.fields); + return std::tie(name, id_attribute, name_attribute, url, action, is_form_tag, + is_formless_checkout, fields) < + std::tie(form.name, form.id_attribute, form.name_attribute, form.url, + form.action, form.is_form_tag, form.is_formless_checkout, + form.fields); } std::ostream& operator<<(std::ostream& os, const FormData& form) { @@ -167,15 +169,6 @@ void SerializeFormData(const FormData& form_data, base::Pickle* pickle) { pickle->WriteString(form_data.main_frame_origin.Serialize()); } -void SerializeFormDataToBase64String(const FormData& form_data, - std::string* output) { - base::Pickle pickle; - SerializeFormData(form_data, &pickle); - Base64Encode( - base::StringPiece(static_cast<const char*>(pickle.data()), pickle.size()), - output); -} - bool DeserializeFormData(base::PickleIterator* iter, FormData* form_data) { int version; FormData temp_form_data; @@ -239,15 +232,26 @@ bool DeserializeFormData(base::PickleIterator* iter, FormData* form_data) { return true; } -bool DeserializeFormDataFromBase64String(const base::StringPiece& input, - FormData* form_data) { - if (input.empty()) - return false; - std::string pickle_data; - Base64Decode(input, &pickle_data); - base::Pickle pickle(pickle_data.data(), static_cast<int>(pickle_data.size())); - base::PickleIterator iter(pickle); - return DeserializeFormData(&iter, form_data); +LogBuffer& operator<<(LogBuffer& buffer, const FormData& form) { + buffer << Tag{"div"} << Attrib{"class", "form"}; + buffer << Tag{"table"}; + buffer << Tr{} << "Form name:" << form.name; + buffer << Tr{} << "Unique renderer Id:" << form.unique_renderer_id; + buffer << Tr{} << "URL:" << form.url; + buffer << Tr{} << "Action:" << form.action; + buffer << Tr{} << "Is action empty:" << form.is_action_empty; + buffer << Tr{} << "Is <form> tag:" << form.is_form_tag; + for (size_t i = 0; i < form.fields.size(); ++i) { + buffer << Tag{"tr"}; + buffer << Tag{"td"} << "Field " << i << ": " << CTag{}; + buffer << Tag{"td"}; + buffer << Tag{"table"} << form.fields.at(i) << CTag{"table"}; + buffer << CTag{"td"}; + buffer << CTag{"tr"}; + } + buffer << CTag{"table"}; + buffer << CTag{"div"}; + return buffer; } } // namespace autofill diff --git a/chromium/components/autofill/core/common/form_data.h b/chromium/components/autofill/core/common/form_data.h index 639135adbb2..932394bc09e 100644 --- a/chromium/components/autofill/core/common/form_data.h +++ b/chromium/components/autofill/core/common/form_data.h @@ -18,6 +18,8 @@ namespace autofill { +class LogBuffer; + // Pair of a button title (e.g. "Register") and its type (e.g. // INPUT_ELEMENT_SUBMIT_TYPE). using ButtonTitleInfo = std::pair<base::string16, mojom::ButtonTitleType>; @@ -27,11 +29,16 @@ using ButtonTitleList = std::vector<ButtonTitleInfo>; // Holds information about a form to be filled and/or submitted. struct FormData { + // TODO(https://crbug.com/875768): Rename this const to kNotSetRendererId, and + // use it also for not set renderer ids in FormFieldData. static constexpr uint32_t kNotSetFormRendererId = std::numeric_limits<uint32_t>::max(); FormData(); - FormData(const FormData& data); + FormData(const FormData&); + FormData& operator=(const FormData&); + FormData(FormData&&); + FormData& operator=(FormData&&); ~FormData(); // Returns true if two forms are the same, not counting the values of the @@ -71,15 +78,19 @@ struct FormData { GURL url; // The action target of the form. GURL action; + // If the form in the DOM has an empty action attribute, the |action| field in + // the FormData is set to the frame URL of the embedding document. This field + // indicates whether the action attribute is empty in the form in the DOM. + bool is_action_empty = false; // The URL of main frame containing this form. url::Origin main_frame_origin; // True if this form is a form tag. - bool is_form_tag; + bool is_form_tag = true; // True if the form is made of unowned fields (i.e., not within a <form> tag) // in what appears to be a checkout flow. This attribute is only calculated // and used if features::kAutofillRestrictUnownedFieldsToFormlessCheckout is // enabled, to prevent heuristics from running on formless non-checkout. - bool is_formless_checkout; + bool is_formless_checkout = false; // Unique renderer id which is returned by function // WebFormElement::UniqueRendererFormId(). It is not persistant between page // loads, so it is not saved and not used in comparison in SameFormAs(). @@ -111,14 +122,7 @@ void SerializeFormData(const FormData& form_data, base::Pickle* pickle); // the part of a pickle created by SerializeFormData. Returns true on success. bool DeserializeFormData(base::PickleIterator* iter, FormData* form_data); -// Serialize FormData. Used by the PasswordManager to persist FormData -// pertaining to password forms in base64 string. It is useful since in some -// cases we need to store C strings without embedded '\0' symbols. -void SerializeFormDataToBase64String(const FormData& form_data, - std::string* output); -// Deserialize FormData. Returns true on success. -bool DeserializeFormDataFromBase64String(const base::StringPiece& input, - FormData* form_data); +LogBuffer& operator<<(LogBuffer& buffer, const FormData& form); } // namespace autofill diff --git a/chromium/components/autofill/core/common/form_data_predictions.cc b/chromium/components/autofill/core/common/form_data_predictions.cc index 9c71a970b98..bc97e87e381 100644 --- a/chromium/components/autofill/core/common/form_data_predictions.cc +++ b/chromium/components/autofill/core/common/form_data_predictions.cc @@ -6,23 +6,24 @@ namespace autofill { -FormDataPredictions::FormDataPredictions() { -} +FormDataPredictions::FormDataPredictions() = default; -FormDataPredictions::FormDataPredictions(const FormDataPredictions& other) - : data(other.data), - signature(other.signature), - fields(other.fields) { -} +FormDataPredictions::FormDataPredictions(const FormDataPredictions&) = default; -FormDataPredictions::~FormDataPredictions() { -} +FormDataPredictions& FormDataPredictions::operator=( + const FormDataPredictions&) = default; + +FormDataPredictions::FormDataPredictions(FormDataPredictions&&) = default; + +FormDataPredictions& FormDataPredictions::operator=(FormDataPredictions&&) = + default; + +FormDataPredictions::~FormDataPredictions() = default; bool FormDataPredictions::operator==( const FormDataPredictions& predictions) const { return (data.SameFormAs(predictions.data) && - signature == predictions.signature && - fields == predictions.fields); + signature == predictions.signature && fields == predictions.fields); } bool FormDataPredictions::operator!=( diff --git a/chromium/components/autofill/core/common/form_data_predictions.h b/chromium/components/autofill/core/common/form_data_predictions.h index 164778a5b22..cc5f9cf9185 100644 --- a/chromium/components/autofill/core/common/form_data_predictions.h +++ b/chromium/components/autofill/core/common/form_data_predictions.h @@ -23,7 +23,10 @@ struct FormDataPredictions { std::vector<FormFieldDataPredictions> fields; FormDataPredictions(); - FormDataPredictions(const FormDataPredictions& other); + FormDataPredictions(const FormDataPredictions&); + FormDataPredictions& operator=(const FormDataPredictions&); + FormDataPredictions(FormDataPredictions&&); + FormDataPredictions& operator=(FormDataPredictions&&); ~FormDataPredictions(); // Added for the sake of testing. diff --git a/chromium/components/autofill/core/common/form_data_unittest.cc b/chromium/components/autofill/core/common/form_data_unittest.cc index ff36cae9cfb..eae97e5c202 100644 --- a/chromium/components/autofill/core/common/form_data_unittest.cc +++ b/chromium/components/autofill/core/common/form_data_unittest.cc @@ -121,7 +121,7 @@ void FillInDummyFormData(FormData* data) { data->action = GURL("https://example.com/action"); data->main_frame_origin = url::Origin::Create(GURL("https://origin-example.com")); - data->is_form_tag = true; // Default value. + data->is_form_tag = true; // Default value. data->is_formless_checkout = false; // Default value. FormFieldData field_data; @@ -166,20 +166,6 @@ TEST(FormDataTest, SerializeAndDeserialize) { EXPECT_TRUE(actual.SameFormAs(data)); } -TEST(FormDataTest, SerializeAndDeserializeInStrings) { - FormData data; - FillInDummyFormData(&data); - data.is_form_tag = false; - - std::string serialized_data; - SerializeFormDataToBase64String(data, &serialized_data); - - FormData actual; - EXPECT_TRUE(DeserializeFormDataFromBase64String(serialized_data, &actual)); - - EXPECT_TRUE(actual.SameFormAs(data)); -} - TEST(FormDataTest, Serialize_v1_Deserialize_vCurrent) { FormData data; FillInDummyFormData(&data); diff --git a/chromium/components/autofill/core/common/form_field_data.cc b/chromium/components/autofill/core/common/form_field_data.cc index b8c0f13cb30..1a804330c6c 100644 --- a/chromium/components/autofill/core/common/form_field_data.cc +++ b/chromium/components/autofill/core/common/form_field_data.cc @@ -8,6 +8,7 @@ #include "base/strings/string_util.h" #include "components/autofill/core/common/autofill_features.h" #include "components/autofill/core/common/autofill_util.h" +#include "components/autofill/core/common/logging/log_buffer.h" // TODO(crbug/897756): Clean up the (de)serialization code. @@ -143,9 +144,15 @@ bool HaveSameLabel(const FormFieldData& field1, const FormFieldData& field2) { FormFieldData::FormFieldData() = default; -FormFieldData::FormFieldData(const FormFieldData& other) = default; +FormFieldData::FormFieldData(const FormFieldData&) = default; -FormFieldData::~FormFieldData() {} +FormFieldData& FormFieldData::operator=(const FormFieldData&) = default; + +FormFieldData::FormFieldData(FormFieldData&&) = default; + +FormFieldData& FormFieldData::operator=(FormFieldData&&) = default; + +FormFieldData::~FormFieldData() = default; bool FormFieldData::SameFieldAs(const FormFieldData& field) const { // TODO(crbug.com/896689): On iOS the unique_id member uniquely addresses @@ -205,6 +212,22 @@ bool FormFieldData::IsTextInputElement() const { form_control_type == "url" || form_control_type == "email"; } +bool FormFieldData::IsPasswordInputElement() const { + return form_control_type == "password"; +} + +bool FormFieldData::DidUserType() const { + return properties_mask & USER_TYPED; +} + +bool FormFieldData::HadFocus() const { + return properties_mask & HAD_FOCUS; +} + +bool FormFieldData::WasAutofilled() const { + return properties_mask & AUTOFILLED; +} + bool FormFieldData::operator==(const FormFieldData& field) const { return SameFieldAs(field) && unique_renderer_id == field.unique_renderer_id && form_control_ax_id == field.form_control_ax_id && @@ -468,4 +491,26 @@ std::ostream& operator<<(std::ostream& os, const FormFieldData& field) { << "label_source=" << field.label_source; } +LogBuffer& operator<<(LogBuffer& buffer, const FormFieldData& field) { + buffer << Tag{"table"}; + buffer << Tr{} << "Name:" << field.name; + buffer << Tr{} << "Unique renderer Id:" << field.unique_renderer_id; + buffer << Tr{} << "Name attribute:" << field.name_attribute; + buffer << Tr{} << "Id attribute:" << field.id_attribute; + constexpr size_t kMaxLabelSize = 100; + const base::string16 truncated_label = + field.label.substr(0, std::min(field.label.length(), kMaxLabelSize)); + buffer << Tr{} << "Label:" << truncated_label; + buffer << Tr{} << "Form control type:" << field.form_control_type; + buffer << Tr{} << "Autocomplete attribute:" << field.autocomplete_attribute; + buffer << Tr{} << "Aria label:" << field.aria_label; + buffer << Tr{} << "Aria description:" << field.aria_description; + buffer << Tr{} << "Section:" << field.section; + buffer << Tr{} << "Is focusable:" << field.is_focusable; + buffer << Tr{} << "Is enabled:" << field.is_enabled; + buffer << Tr{} << "Is readonly:" << field.is_readonly; + buffer << CTag{"table"}; + return buffer; +} + } // namespace autofill diff --git a/chromium/components/autofill/core/common/form_field_data.h b/chromium/components/autofill/core/common/form_field_data.h index b9d4f9d6013..605f9e0baad 100644 --- a/chromium/components/autofill/core/common/form_field_data.h +++ b/chromium/components/autofill/core/common/form_field_data.h @@ -18,10 +18,12 @@ namespace base { class Pickle; class PickleIterator; -} +} // namespace base namespace autofill { +class LogBuffer; + // The flags describing form field properties. enum FieldPropertiesFlags { NO_FLAGS = 0u, @@ -59,7 +61,10 @@ struct FormFieldData { std::numeric_limits<uint32_t>::max(); FormFieldData(); - FormFieldData(const FormFieldData& other); + FormFieldData(const FormFieldData&); + FormFieldData& operator=(const FormFieldData&); + FormFieldData(FormFieldData&&); + FormFieldData& operator=(FormFieldData&&); ~FormFieldData(); // Returns true if two form fields are the same, not counting the value. @@ -81,11 +86,19 @@ struct FormFieldData { // a textarea. bool IsTextInputElement() const; + bool IsPasswordInputElement() const; + // Returns true if the field is visible to the user. bool IsVisible() const { return is_focusable && role != RoleAttribute::kPresentation; } + // These functions do not work for Autofill code. + // TODO(https://crbug.com/1006745): Fix this. + bool DidUserType() const; + bool HadFocus() const; + bool WasAutofilled() const; + // Note: operator==() performs a full-field-comparison(byte by byte), this is // different from SameFieldAs(), which ignores comparison for those "values" // not regarded as part of identity of the field, such as is_autofilled and @@ -202,6 +215,9 @@ std::ostream& operator<<(std::ostream& os, const FormFieldData& field); EXPECT_EQ(expected.name_attribute, actual.name_attribute); \ } while (0) +// Produces a <table> element with information about the form. +LogBuffer& operator<<(LogBuffer& buffer, const FormFieldData& form); + } // namespace autofill #endif // COMPONENTS_AUTOFILL_CORE_COMMON_FORM_FIELD_DATA_H_ diff --git a/chromium/components/autofill/core/common/form_field_data_predictions.cc b/chromium/components/autofill/core/common/form_field_data_predictions.cc index 9109fcfd537..46e8bd2b6e0 100644 --- a/chromium/components/autofill/core/common/form_field_data_predictions.cc +++ b/chromium/components/autofill/core/common/form_field_data_predictions.cc @@ -6,21 +6,21 @@ namespace autofill { -FormFieldDataPredictions::FormFieldDataPredictions() { -} +FormFieldDataPredictions::FormFieldDataPredictions() = default; FormFieldDataPredictions::FormFieldDataPredictions( - const FormFieldDataPredictions& other) - : field(other.field), - signature(other.signature), - heuristic_type(other.heuristic_type), - server_type(other.server_type), - overall_type(other.overall_type), - parseable_name(other.parseable_name), - section(other.section) {} - -FormFieldDataPredictions::~FormFieldDataPredictions() { -} + const FormFieldDataPredictions&) = default; + +FormFieldDataPredictions& FormFieldDataPredictions::operator=( + const FormFieldDataPredictions&) = default; + +FormFieldDataPredictions::FormFieldDataPredictions(FormFieldDataPredictions&&) = + default; + +FormFieldDataPredictions& FormFieldDataPredictions::operator=( + FormFieldDataPredictions&&) = default; + +FormFieldDataPredictions::~FormFieldDataPredictions() = default; bool FormFieldDataPredictions::operator==( const FormFieldDataPredictions& predictions) const { diff --git a/chromium/components/autofill/core/common/form_field_data_predictions.h b/chromium/components/autofill/core/common/form_field_data_predictions.h index baeb68bfba8..1792ddc512e 100644 --- a/chromium/components/autofill/core/common/form_field_data_predictions.h +++ b/chromium/components/autofill/core/common/form_field_data_predictions.h @@ -15,7 +15,10 @@ namespace autofill { // Stores information about a field in a form. struct FormFieldDataPredictions { FormFieldDataPredictions(); - FormFieldDataPredictions(const FormFieldDataPredictions& other); + FormFieldDataPredictions(const FormFieldDataPredictions&); + FormFieldDataPredictions& operator=(const FormFieldDataPredictions&); + FormFieldDataPredictions(FormFieldDataPredictions&&); + FormFieldDataPredictions& operator=(FormFieldDataPredictions&&); ~FormFieldDataPredictions(); FormFieldData field; diff --git a/chromium/components/autofill/core/browser/logging/log_buffer.cc b/chromium/components/autofill/core/common/logging/log_buffer.cc index 8bbd7a9df7b..5abd4daa207 100644 --- a/chromium/components/autofill/core/browser/logging/log_buffer.cc +++ b/chromium/components/autofill/core/common/logging/log_buffer.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "components/autofill/core/browser/logging/log_buffer.h" +#include "components/autofill/core/common/logging/log_buffer.h" #include <string> @@ -40,7 +40,7 @@ void AppendChildToLastNode(std::vector<base::Value>* buffer, DCHECK(!IsTextNode(parent)); if (auto* children = parent.FindListKey("children")) { - children->GetList().push_back(std::move(new_child)); + children->Append(std::move(new_child)); return; } diff --git a/chromium/components/autofill/core/browser/logging/log_buffer.h b/chromium/components/autofill/core/common/logging/log_buffer.h index 2dd9c4c74b4..fc782f4f93d 100644 --- a/chromium/components/autofill/core/browser/logging/log_buffer.h +++ b/chromium/components/autofill/core/common/logging/log_buffer.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_LOGGING_LOG_BUFFER_H_ -#define COMPONENTS_AUTOFILL_CORE_BROWSER_LOGGING_LOG_BUFFER_H_ +#ifndef COMPONENTS_AUTOFILL_CORE_COMMON_LOGGING_LOG_BUFFER_H_ +#define COMPONENTS_AUTOFILL_CORE_COMMON_LOGGING_LOG_BUFFER_H_ #include <string> #include <type_traits> @@ -14,7 +14,6 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/string_piece.h" #include "base/values.h" -#include "third_party/protobuf/src/google/protobuf/repeated_field.h" #include "url/gurl.h" // The desired pattern to generate log messages is to pass a scope, a log @@ -154,19 +153,6 @@ LogBuffer& operator<<(LogBuffer& buf, LogBuffer&& buffer); LogBuffer& operator<<(LogBuffer& buf, const GURL& url); template <typename T> -LogBuffer& operator<<(LogBuffer& buf, - const ::google::protobuf::RepeatedField<T>& values) { - buf << "["; - for (int i = 0; i < values.size(); ++i) { - if (i) - buf << ", "; - buf << values.Get(i); - } - buf << "]"; - return buf; -} - -template <typename T> LogBuffer& operator<<(LogBuffer& buf, const std::vector<T>& values) { buf << "["; for (size_t i = 0; i < values.size(); ++i) { @@ -219,4 +205,4 @@ LogBuffer HighlightValue(base::StringPiece16 haystack, } // namespace autofill -#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_LOGGING_LOG_BUFFER_H_ +#endif // COMPONENTS_AUTOFILL_CORE_COMMON_LOGGING_LOG_BUFFER_H_ diff --git a/chromium/components/autofill/core/browser/logging/log_buffer_unittest.cc b/chromium/components/autofill/core/common/logging/log_buffer_unittest.cc index 3b4b6c94ab6..de9126e263b 100644 --- a/chromium/components/autofill/core/browser/logging/log_buffer_unittest.cc +++ b/chromium/components/autofill/core/common/logging/log_buffer_unittest.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "components/autofill/core/browser/logging/log_buffer.h" +#include "components/autofill/core/common/logging/log_buffer.h" #include "base/json/json_writer.h" #include "base/strings/string_piece.h" diff --git a/chromium/components/autofill/core/common/mojom/autofill_types.mojom b/chromium/components/autofill/core/common/mojom/autofill_types.mojom index 8e8a3c3c44d..0879248916c 100644 --- a/chromium/components/autofill/core/common/mojom/autofill_types.mojom +++ b/chromium/components/autofill/core/common/mojom/autofill_types.mojom @@ -153,6 +153,7 @@ struct FormData { array<ButtonTitleInfo> button_titles; url.mojom.Url url; url.mojom.Url action; + bool is_action_empty; url.mojom.Origin main_frame_origin; bool is_form_tag; bool is_formless_checkout; @@ -213,6 +214,7 @@ struct PasswordGenerationUIData { int32 max_length; mojo_base.mojom.String16 generation_element; uint32 generation_element_id; + bool is_generation_element_password_type; mojo_base.mojom.TextDirection text_direction; PasswordForm password_form; }; @@ -295,16 +297,23 @@ struct PasswordForm { bool only_for_fallback; }; -// Note: Even though https://crbug.com/628104 is solved, we still can not use a -// map directly as long as https://crbug.com/914074 is not fixed. -struct PasswordFormFieldPredictionMap { - array<FormFieldData> keys; - array<PasswordFormFieldPredictionType> values; +// autofill::ParsingResult +struct ParsingResult { + uint32 username_renderer_id; + uint32 password_renderer_id; + uint32 new_password_renderer_id; + uint32 confirm_password_renderer_id; }; -// Note: Even though https://crbug.com/628104 is solved, we still can not use a -// map directly as long as https://crbug.com/914074 is not fixed. -struct FormsPredictionsMap { - array<FormData> keys; - array<PasswordFormFieldPredictionMap> values; +// Represents the autofill state. +enum AutofillState { + // There are no available suggestions, neither autofill nor autocomplete, for + // the input. + kNoSuggestions, + // There are available autofill suggestions for the input. Autofill fills in + // an entire form. + kAutofillAvailable, + // There are available autocomplete suggestions for the input. Autocomplete + // only fills in a single input. + kAutocompleteAvailable, }; diff --git a/chromium/components/autofill/core/common/mojom/autofill_types.typemap b/chromium/components/autofill/core/common/mojom/autofill_types.typemap index 6b444c359f4..1531e37fb14 100644 --- a/chromium/components/autofill/core/common/mojom/autofill_types.typemap +++ b/chromium/components/autofill/core/common/mojom/autofill_types.typemap @@ -9,7 +9,6 @@ public_headers = [ "//components/autofill/core/common/form_field_data.h", "//components/autofill/core/common/form_field_data_predictions.h", "//components/autofill/core/common/password_form.h", - "//components/autofill/core/common/password_form_field_prediction_map.h", "//components/autofill/core/common/password_form_fill_data.h", "//components/autofill/core/common/password_form_generation_data.h", "//components/autofill/core/common/password_generation_util.h", @@ -32,6 +31,7 @@ type_mappings = [ "autofill.mojom.FormFieldData=::autofill::FormFieldData", "autofill.mojom.FormFieldDataPredictions=::autofill::FormFieldDataPredictions", "autofill.mojom.FormsPredictionsMap=::autofill::FormsPredictionsMap", + "autofill.mojom.ParsingResult=::autofill::ParsingResult", "autofill.mojom.PasswordAndRealm=::autofill::PasswordAndRealm", "autofill.mojom.PasswordForm=::autofill::PasswordForm", "autofill.mojom.PasswordFormFieldPredictionMap=::autofill::PasswordFormFieldPredictionMap", diff --git a/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits.cc b/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits.cc index 9128a8523fc..3962a512072 100644 --- a/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits.cc +++ b/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits.cc @@ -107,6 +107,7 @@ bool StructTraits<autofill::mojom::FormDataDataView, autofill::FormData>::Read( return false; if (!data.ReadAction(&out->action)) return false; + out->is_action_empty = data.is_action_empty(); if (!data.ReadMainFrameOrigin(&out->main_frame_origin)) return false; @@ -222,6 +223,8 @@ bool StructTraits<autofill::mojom::PasswordGenerationUIDataDataView, out->max_length = data.max_length(); out->generation_element_id = data.generation_element_id(); + out->is_generation_element_password_type = + data.is_generation_element_password_type(); if (!data.ReadGenerationElement(&out->generation_element) || !data.ReadTextDirection(&out->text_direction) || @@ -298,92 +301,6 @@ bool StructTraits< } // static -std::vector<autofill::FormFieldData> -StructTraits<autofill::mojom::PasswordFormFieldPredictionMapDataView, - autofill::PasswordFormFieldPredictionMap>:: - keys(const autofill::PasswordFormFieldPredictionMap& r) { - std::vector<autofill::FormFieldData> data; - for (const auto& i : r) - data.push_back(i.first); - return data; -} - -// static -std::vector<autofill::mojom::PasswordFormFieldPredictionType> -StructTraits<autofill::mojom::PasswordFormFieldPredictionMapDataView, - autofill::PasswordFormFieldPredictionMap>:: - values(const autofill::PasswordFormFieldPredictionMap& r) { - std::vector<autofill::mojom::PasswordFormFieldPredictionType> types; - for (const auto& i : r) - types.push_back(i.second); - return types; -} - -// static -bool StructTraits<autofill::mojom::PasswordFormFieldPredictionMapDataView, - autofill::PasswordFormFieldPredictionMap>:: - Read(autofill::mojom::PasswordFormFieldPredictionMapDataView data, - autofill::PasswordFormFieldPredictionMap* out) { - // Combines keys vector and values vector to the map. - std::vector<autofill::FormFieldData> keys; - if (!data.ReadKeys(&keys)) - return false; - std::vector<autofill::mojom::PasswordFormFieldPredictionType> values; - if (!data.ReadValues(&values)) - return false; - if (keys.size() != values.size()) - return false; - out->clear(); - for (size_t i = 0; i < keys.size(); ++i) - out->insert({keys[i], values[i]}); - - return true; -} - -// static -std::vector<autofill::FormData> StructTraits< - autofill::mojom::FormsPredictionsMapDataView, - autofill::FormsPredictionsMap>::keys(const autofill::FormsPredictionsMap& - r) { - std::vector<autofill::FormData> data; - for (const auto& i : r) - data.push_back(i.first); - return data; -} - -// static -std::vector<autofill::PasswordFormFieldPredictionMap> StructTraits< - autofill::mojom::FormsPredictionsMapDataView, - autofill::FormsPredictionsMap>::values(const autofill::FormsPredictionsMap& - r) { - std::vector<autofill::PasswordFormFieldPredictionMap> maps; - for (const auto& i : r) - maps.push_back(i.second); - return maps; -} - -// static -bool StructTraits<autofill::mojom::FormsPredictionsMapDataView, - autofill::FormsPredictionsMap>:: - Read(autofill::mojom::FormsPredictionsMapDataView data, - autofill::FormsPredictionsMap* out) { - // Combines keys vector and values vector to the map. - std::vector<autofill::FormData> keys; - if (!data.ReadKeys(&keys)) - return false; - std::vector<autofill::PasswordFormFieldPredictionMap> values; - if (!data.ReadValues(&values)) - return false; - if (keys.size() != values.size()) - return false; - out->clear(); - for (size_t i = 0; i < keys.size(); ++i) - out->insert({keys[i], values[i]}); - - return true; -} - -// static bool StructTraits<autofill::mojom::ValueElementPairDataView, autofill::ValueElementPair>:: Read(autofill::mojom::ValueElementPairDataView data, @@ -394,4 +311,16 @@ bool StructTraits<autofill::mojom::ValueElementPairDataView, return true; } +bool StructTraits< + autofill::mojom::ParsingResultDataView, + autofill::ParsingResult>::Read(autofill::mojom::ParsingResultDataView data, + autofill::ParsingResult* out) { + out->username_renderer_id = data.username_renderer_id(); + out->password_renderer_id = data.password_renderer_id(); + out->new_password_renderer_id = data.new_password_renderer_id(); + out->confirm_password_renderer_id = data.confirm_password_renderer_id(); + + return true; +} + } // namespace mojo diff --git a/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits.h b/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits.h index 6a1ca29db05..a26fbd21849 100644 --- a/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits.h +++ b/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits.h @@ -18,7 +18,6 @@ #include "components/autofill/core/common/form_field_data_predictions.h" #include "components/autofill/core/common/mojom/autofill_types.mojom.h" #include "components/autofill/core/common/password_form.h" -#include "components/autofill/core/common/password_form_field_prediction_map.h" #include "components/autofill/core/common/password_form_fill_data.h" #include "components/autofill/core/common/password_form_generation_data.h" #include "components/autofill/core/common/password_generation_util.h" @@ -197,6 +196,10 @@ struct StructTraits<autofill::mojom::FormDataDataView, autofill::FormData> { static const GURL& action(const autofill::FormData& r) { return r.action; } + static bool is_action_empty(const autofill::FormData& r) { + return r.is_action_empty; + } + static const url::Origin& main_frame_origin(const autofill::FormData& r) { return r.main_frame_origin; } @@ -405,6 +408,11 @@ struct StructTraits<autofill::mojom::PasswordGenerationUIDataDataView, return r.generation_element_id; } + static bool is_generation_element_password_type( + const autofill::password_generation::PasswordGenerationUIData& r) { + return r.is_generation_element_password_type; + } + static base::i18n::TextDirection text_direction( const autofill::password_generation::PasswordGenerationUIData& r) { return r.text_direction; @@ -578,32 +586,6 @@ struct StructTraits<autofill::mojom::PasswordFormDataView, }; template <> -struct StructTraits<autofill::mojom::PasswordFormFieldPredictionMapDataView, - autofill::PasswordFormFieldPredictionMap> { - static std::vector<autofill::FormFieldData> keys( - const autofill::PasswordFormFieldPredictionMap& r); - - static std::vector<autofill::mojom::PasswordFormFieldPredictionType> values( - const autofill::PasswordFormFieldPredictionMap& r); - - static bool Read(autofill::mojom::PasswordFormFieldPredictionMapDataView data, - autofill::PasswordFormFieldPredictionMap* out); -}; - -template <> -struct StructTraits<autofill::mojom::FormsPredictionsMapDataView, - autofill::FormsPredictionsMap> { - static std::vector<autofill::FormData> keys( - const autofill::FormsPredictionsMap& r); - - static std::vector<autofill::PasswordFormFieldPredictionMap> values( - const autofill::FormsPredictionsMap& r); - - static bool Read(autofill::mojom::FormsPredictionsMapDataView data, - autofill::FormsPredictionsMap* out); -}; - -template <> struct StructTraits<autofill::mojom::ValueElementPairDataView, autofill::ValueElementPair> { static base::string16 value(const autofill::ValueElementPair& r) { @@ -618,6 +600,30 @@ struct StructTraits<autofill::mojom::ValueElementPairDataView, autofill::ValueElementPair* out); }; +template <> +struct StructTraits<autofill::mojom::ParsingResultDataView, + autofill::ParsingResult> { + static uint32_t username_renderer_id(const autofill::ParsingResult& r) { + return r.username_renderer_id; + } + + static uint32_t password_renderer_id(const autofill::ParsingResult& r) { + return r.password_renderer_id; + } + + static uint32_t new_password_renderer_id(const autofill::ParsingResult& r) { + return r.new_password_renderer_id; + } + + static uint32_t confirm_password_renderer_id( + const autofill::ParsingResult& r) { + return r.confirm_password_renderer_id; + } + + static bool Read(autofill::mojom::ParsingResultDataView data, + autofill::ParsingResult* out); +}; + } // namespace mojo #endif // COMPONENTS_AUTOFILL_CORE_COMMON_MOJOM_AUTOFILL_TYPES_MOJOM_TRAITS_H_ diff --git a/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits_unittest.cc b/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits_unittest.cc index 37c68ad829a..1fd64be2cfc 100644 --- a/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits_unittest.cc +++ b/chromium/components/autofill/core/common/mojom/autofill_types_mojom_traits_unittest.cc @@ -9,13 +9,15 @@ #include "base/strings/utf_string_conversions.h" #include "base/test/task_environment.h" #include "components/autofill/core/browser/autofill_test_utils.h" +#include "components/autofill/core/common/autofill_clock.h" #include "components/autofill/core/common/form_data.h" #include "components/autofill/core/common/form_field_data.h" #include "components/autofill/core/common/mojom/test_autofill_types.mojom.h" #include "components/autofill/core/common/password_generation_util.h" #include "components/autofill/core/common/signatures_util.h" -#include "mojo/public/cpp/bindings/binding_set.h" -#include "mojo/public/cpp/bindings/interface_request.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/receiver_set.h" +#include "mojo/public/cpp/bindings/remote.h" #include "testing/gtest/include/gtest/gtest.h" namespace autofill { @@ -90,8 +92,8 @@ void CreateTestPasswordForm(PasswordForm* form) { form->new_password_marked_by_site = false; form->new_password_element = base::ASCIIToUTF16("confirmation_password"); form->preferred = false; - form->date_created = base::Time::Now(); - form->date_synced = base::Time::Now(); + form->date_created = AutofillClock::Now(); + form->date_synced = AutofillClock::Now(); form->blacklisted_by_user = false; form->type = PasswordForm::Type::kGenerated; form->times_used = 999; @@ -109,45 +111,13 @@ void CreateTestPasswordForm(PasswordForm* form) { mojom::SubmissionIndicatorEvent::SAME_DOCUMENT_NAVIGATION; } -void CreateTestFormsPredictionsMap(FormsPredictionsMap* predictions) { - FormsPredictionsMap& result_map = *predictions; - // 1st element. - FormData form_data; - test::CreateTestAddressFormData(&form_data); - ASSERT_TRUE(form_data.fields.size() >= 4); - result_map[form_data][form_data.fields[0]] = - PasswordFormFieldPredictionType::kUsername; - result_map[form_data][form_data.fields[1]] = - PasswordFormFieldPredictionType::kCurrentPassword; - result_map[form_data][form_data.fields[2]] = - PasswordFormFieldPredictionType::kNewPassword; - result_map[form_data][form_data.fields[3]] = - PasswordFormFieldPredictionType::kNotPassword; - - // 2nd element. - form_data.fields.clear(); - result_map[form_data] = {}; - - // 3rd element. - FormFieldData field_data; - test::CreateTestSelectField("TestLabel1", "TestName1", "TestValue1", kOptions, - kOptions, 4, &field_data); - form_data.fields.push_back(field_data); - test::CreateTestSelectField("TestLabel2", "TestName2", "TestValue2", kOptions, - kOptions, 4, &field_data); - form_data.fields.push_back(field_data); - result_map[form_data][form_data.fields[0]] = - PasswordFormFieldPredictionType::kNewPassword; - result_map[form_data][form_data.fields[1]] = - PasswordFormFieldPredictionType::kCurrentPassword; -} - void CreatePasswordGenerationUIData( password_generation::PasswordGenerationUIData* data) { data->bounds = gfx::RectF(1, 1, 200, 100); data->max_length = 20; data->generation_element = base::ASCIIToUTF16("generation_element"); data->text_direction = base::i18n::RIGHT_TO_LEFT; + data->is_generation_element_password_type = false; CreateTestPasswordForm(&data->password_form); } @@ -185,6 +155,8 @@ void CheckEqualPassPasswordGenerationUIData( EXPECT_EQ(expected.bounds, actual.bounds); EXPECT_EQ(expected.max_length, actual.max_length); EXPECT_EQ(expected.generation_element, actual.generation_element); + EXPECT_EQ(expected.is_generation_element_password_type, + actual.is_generation_element_password_type); EXPECT_EQ(expected.text_direction, actual.text_direction); EXPECT_EQ(expected.password_form, actual.password_form); } @@ -196,10 +168,10 @@ class AutofillTypeTraitsTestImpl : public testing::Test, public: AutofillTypeTraitsTestImpl() {} - mojom::TypeTraitsTestPtr GetTypeTraitsTestProxy() { - mojom::TypeTraitsTestPtr proxy; - bindings_.AddBinding(this, mojo::MakeRequest(&proxy)); - return proxy; + mojo::PendingRemote<mojom::TypeTraitsTest> GetTypeTraitsTestRemote() { + mojo::PendingRemote<mojom::TypeTraitsTest> remote; + receivers_.Add(this, remote.InitWithNewPipeAndPassReceiver()); + return remote; } // mojom::TypeTraitsTest: @@ -247,16 +219,10 @@ class AutofillTypeTraitsTestImpl : public testing::Test, std::move(callback).Run(s); } - void PassFormsPredictionsMap( - const FormsPredictionsMap& s, - PassFormsPredictionsMapCallback callback) override { - std::move(callback).Run(s); - } - private: base::test::TaskEnvironment task_environment_; - mojo::BindingSet<TypeTraitsTest> bindings_; + mojo::ReceiverSet<TypeTraitsTest> receivers_; }; void ExpectFormFieldData(const FormFieldData& expected, @@ -321,13 +287,6 @@ void ExpectPasswordForm(const PasswordForm& expected, std::move(closure).Run(); } -void ExpectFormsPredictionsMap(const FormsPredictionsMap& expected, - base::OnceClosure closure, - const FormsPredictionsMap& passed) { - EXPECT_EQ(expected, passed); - std::move(closure).Run(); -} - TEST_F(AutofillTypeTraitsTestImpl, PassFormFieldData) { FormFieldData input; test::CreateTestSelectField("TestLabel", "TestName", "TestValue", kOptions, @@ -350,8 +309,8 @@ TEST_F(AutofillTypeTraitsTestImpl, PassFormFieldData) { input.typed_value = base::ASCIIToUTF16("TestTypedValue"); base::RunLoop loop; - mojom::TypeTraitsTestPtr proxy = GetTypeTraitsTestProxy(); - proxy->PassFormFieldData( + mojo::Remote<mojom::TypeTraitsTest> remote(GetTypeTraitsTestRemote()); + remote->PassFormFieldData( input, base::BindOnce(&ExpectFormFieldData, input, loop.QuitClosure())); loop.Run(); } @@ -365,8 +324,8 @@ TEST_F(AutofillTypeTraitsTestImpl, PassFormData) { mojom::ButtonTitleType::BUTTON_ELEMENT_SUBMIT_TYPE)); base::RunLoop loop; - mojom::TypeTraitsTestPtr proxy = GetTypeTraitsTestProxy(); - proxy->PassFormData( + mojo::Remote<mojom::TypeTraitsTest> remote(GetTypeTraitsTestRemote()); + remote->PassFormData( input, base::BindOnce(&ExpectFormData, input, loop.QuitClosure())); loop.Run(); } @@ -376,8 +335,8 @@ TEST_F(AutofillTypeTraitsTestImpl, PassFormFieldDataPredictions) { CreateTestFieldDataPredictions("TestSignature", &input); base::RunLoop loop; - mojom::TypeTraitsTestPtr proxy = GetTypeTraitsTestProxy(); - proxy->PassFormFieldDataPredictions( + mojo::Remote<mojom::TypeTraitsTest> remote(GetTypeTraitsTestRemote()); + remote->PassFormFieldDataPredictions( input, base::BindOnce(&ExpectFormFieldDataPredictions, input, loop.QuitClosure())); loop.Run(); @@ -397,8 +356,8 @@ TEST_F(AutofillTypeTraitsTestImpl, PassFormDataPredictions) { input.fields.push_back(field_predict); base::RunLoop loop; - mojom::TypeTraitsTestPtr proxy = GetTypeTraitsTestProxy(); - proxy->PassFormDataPredictions( + mojo::Remote<mojom::TypeTraitsTest> remote(GetTypeTraitsTestRemote()); + remote->PassFormDataPredictions( input, base::BindOnce(&ExpectFormDataPredictions, input, loop.QuitClosure())); loop.Run(); @@ -409,8 +368,8 @@ TEST_F(AutofillTypeTraitsTestImpl, PassPasswordFormFillData) { CreateTestPasswordFormFillData(&input); base::RunLoop loop; - mojom::TypeTraitsTestPtr proxy = GetTypeTraitsTestProxy(); - proxy->PassPasswordFormFillData( + mojo::Remote<mojom::TypeTraitsTest> remote(GetTypeTraitsTestRemote()); + remote->PassPasswordFormFillData( input, base::BindOnce(&ExpectPasswordFormFillData, input, loop.QuitClosure())); loop.Run(); @@ -422,8 +381,8 @@ TEST_F(AutofillTypeTraitsTestImpl, PasswordFormGenerationData) { input.confirmation_password_renderer_id = 5789u; base::RunLoop loop; - mojom::TypeTraitsTestPtr proxy = GetTypeTraitsTestProxy(); - proxy->PassPasswordFormGenerationData( + mojo::Remote<mojom::TypeTraitsTest> remote(GetTypeTraitsTestRemote()); + remote->PassPasswordFormGenerationData( input, base::BindOnce(&ExpectPasswordFormGenerationData, input, loop.QuitClosure())); loop.Run(); @@ -434,8 +393,8 @@ TEST_F(AutofillTypeTraitsTestImpl, PassPasswordGenerationUIData) { CreatePasswordGenerationUIData(&input); base::RunLoop loop; - mojom::TypeTraitsTestPtr proxy = GetTypeTraitsTestProxy(); - proxy->PassPasswordGenerationUIData( + mojo::Remote<mojom::TypeTraitsTest> remote(GetTypeTraitsTestRemote()); + remote->PassPasswordGenerationUIData( input, base::BindOnce(&ExpectPasswordGenerationUIData, input, loop.QuitClosure())); loop.Run(); @@ -446,22 +405,10 @@ TEST_F(AutofillTypeTraitsTestImpl, PassPasswordForm) { CreateTestPasswordForm(&input); base::RunLoop loop; - mojom::TypeTraitsTestPtr proxy = GetTypeTraitsTestProxy(); - proxy->PassPasswordForm( + mojo::Remote<mojom::TypeTraitsTest> remote(GetTypeTraitsTestRemote()); + remote->PassPasswordForm( input, base::BindOnce(&ExpectPasswordForm, input, loop.QuitClosure())); loop.Run(); } -TEST_F(AutofillTypeTraitsTestImpl, PassFormsPredictionsMap) { - FormsPredictionsMap input; - CreateTestFormsPredictionsMap(&input); - - base::RunLoop loop; - mojom::TypeTraitsTestPtr proxy = GetTypeTraitsTestProxy(); - proxy->PassFormsPredictionsMap( - input, - base::BindOnce(&ExpectFormsPredictionsMap, input, loop.QuitClosure())); - loop.Run(); -} - } // namespace autofill diff --git a/chromium/components/autofill/core/common/mojom/test_autofill_types.mojom b/chromium/components/autofill/core/common/mojom/test_autofill_types.mojom index 9f5be9e3b81..dd1392662f1 100644 --- a/chromium/components/autofill/core/common/mojom/test_autofill_types.mojom +++ b/chromium/components/autofill/core/common/mojom/test_autofill_types.mojom @@ -16,8 +16,6 @@ interface TypeTraitsTest { PassPasswordForm(PasswordForm s) => (PasswordForm passed); PassPasswordFormFillData(PasswordFormFillData s) => (PasswordFormFillData passed); - PassFormsPredictionsMap(FormsPredictionsMap s) => - (FormsPredictionsMap passed); PassPasswordFormGenerationData(PasswordFormGenerationData s) => (PasswordFormGenerationData passed); PassPasswordGenerationUIData(PasswordGenerationUIData s) => diff --git a/chromium/components/autofill/core/common/password_form.cc b/chromium/components/autofill/core/common/password_form.cc index fc34f742b52..d6f0044c8ea 100644 --- a/chromium/components/autofill/core/common/password_form.cc +++ b/chromium/components/autofill/core/common/password_form.cc @@ -26,6 +26,17 @@ std::string ToString(const T& obj) { return ostream.str(); } +std::string StoreToString(PasswordForm::Store from_store) { + switch (from_store) { + case PasswordForm::Store::kNotSet: + return "Not Set"; + case PasswordForm::Store::kProfileStore: + return "Profile Store"; + case PasswordForm::Store::kAccountStore: + return "Account Store"; + } +} + // Serializes a PasswordForm to a JSON object. Used only for logging in tests. void PasswordFormToJSON(const PasswordForm& form, base::DictionaryValue* target) { @@ -85,6 +96,7 @@ void PasswordFormToJSON(const PasswordForm& form, target->SetBoolean("is_gaia_with_skip_save_password_form", form.form_data.is_gaia_with_skip_save_password_form); target->SetBoolean("is_new_password_reliable", form.is_new_password_reliable); + target->SetString("from_store", StoreToString(form.from_store)); } } // namespace @@ -136,6 +148,10 @@ bool PasswordForm::IsSingleUsername() const { !HasNewPasswordElement(); } +bool PasswordForm::IsUsingAccountStore() const { + return from_store == Store::kAccountStore; +} + bool PasswordForm::operator==(const PasswordForm& form) const { return scheme == form.scheme && signon_realm == form.signon_realm && origin == form.origin && action == form.action && @@ -180,7 +196,8 @@ bool PasswordForm::operator==(const PasswordForm& form) const { app_icon_url == form.app_icon_url && submission_event == form.submission_event && only_for_fallback == form.only_for_fallback && - is_new_password_reliable == form.is_new_password_reliable; + is_new_password_reliable == form.is_new_password_reliable && + from_store == form.from_store; } bool PasswordForm::operator!=(const PasswordForm& form) const { @@ -196,28 +213,6 @@ bool ArePasswordFormUniqueKeysEqual(const PasswordForm& left, left.password_element == right.password_element); } -bool LessThanUniqueKey::operator()( - const std::unique_ptr<PasswordForm>& left, - const std::unique_ptr<PasswordForm>& right) const { - int result = left->signon_realm.compare(right->signon_realm); - if (result) - return result < 0; - - result = left->username_element.compare(right->username_element); - if (result) - return result < 0; - - result = left->username_value.compare(right->username_value); - if (result) - return result < 0; - - result = left->password_element.compare(right->password_element); - if (result) - return result < 0; - - return left->origin < right->origin; -} - base::string16 ValueElementVectorToString( const ValueElementVector& value_element_pairs) { std::vector<base::string16> pairs(value_element_pairs.size()); @@ -228,12 +223,6 @@ base::string16 ValueElementVectorToString( return base::JoinString(pairs, base::ASCIIToUTF16(", ")); } -bool IsHttpAuthScheme(PasswordForm::Scheme scheme) { - return scheme == PasswordForm::Scheme::kBasic || - scheme == PasswordForm::Scheme::kDigest || - scheme == PasswordForm::Scheme::kOther; -} - std::ostream& operator<<(std::ostream& os, const PasswordForm& form) { base::DictionaryValue form_json; PasswordFormToJSON(form, &form_json); diff --git a/chromium/components/autofill/core/common/password_form.h b/chromium/components/autofill/core/common/password_form.h index 348900b8212..48e63390f8b 100644 --- a/chromium/components/autofill/core/common/password_form.h +++ b/chromium/components/autofill/core/common/password_form.h @@ -303,6 +303,16 @@ struct PasswordForm { // as signal for password generation eligibility. bool is_new_password_reliable = false; + enum class Store { + // Default value. + kNotSet, + // Credential came from the profile (i.e. local) storage. + kProfileStore, + // Credential came from the Gaia-account-scoped storage. + kAccountStore + }; + Store from_store = Store::kNotSet; + // Return true if we consider this form to be a change password form. // We use only client heuristics, so it could include signup forms. bool IsPossibleChangePasswordForm() const; @@ -328,6 +338,10 @@ struct PasswordForm { // not set. bool IsSingleUsername() const; + // Returns whether this form is stored in the account-scoped store, i.e. + // whether |from_store == Store::kAccountStore|. + bool IsUsingAccountStore() const; + // Equality operators for testing. bool operator==(const PasswordForm& form) const; bool operator!=(const PasswordForm& form) const; @@ -346,19 +360,10 @@ struct PasswordForm { bool ArePasswordFormUniqueKeysEqual(const PasswordForm& left, const PasswordForm& right); -// A comparator for the unique key. -struct LessThanUniqueKey { - bool operator()(const std::unique_ptr<PasswordForm>& left, - const std::unique_ptr<PasswordForm>& right) const; -}; - // Converts a vector of ValueElementPair to string. base::string16 ValueElementVectorToString( const ValueElementVector& value_element_pairs); -// Returns true if |scheme| corresponds to http auth scheme. -bool IsHttpAuthScheme(PasswordForm::Scheme scheme); - // For testing. std::ostream& operator<<(std::ostream& os, const PasswordForm& form); std::ostream& operator<<(std::ostream& os, PasswordForm* form); diff --git a/chromium/components/autofill/core/common/password_form_field_prediction_map.h b/chromium/components/autofill/core/common/password_form_field_prediction_map.h deleted file mode 100644 index b0d149c092c..00000000000 --- a/chromium/components/autofill/core/common/password_form_field_prediction_map.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2015 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. - -#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_PASSWORD_FORM_FIELD_PREDICTION_MAP_H_ -#define COMPONENTS_AUTOFILL_CORE_BROWSER_PASSWORD_FORM_FIELD_PREDICTION_MAP_H_ - -#include "base/containers/flat_map.h" -#include "components/autofill/core/common/form_data.h" -#include "components/autofill/core/common/form_field_data.h" -#include "components/autofill/core/common/mojom/autofill_types.mojom-shared.h" - -namespace autofill { - -using PasswordFormFieldPredictionMap = - base::flat_map<FormFieldData, mojom::PasswordFormFieldPredictionType>; -using FormsPredictionsMap = - base::flat_map<FormData, PasswordFormFieldPredictionMap>; - -} // namespace autofill - -#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PASSWORD_FORM_FIELD_PREDICTION_MAP_H_ diff --git a/chromium/components/autofill/core/common/password_form_fill_data.cc b/chromium/components/autofill/core/common/password_form_fill_data.cc index db08ce036bb..06b7eb4de26 100644 --- a/chromium/components/autofill/core/common/password_form_fill_data.cc +++ b/chromium/components/autofill/core/common/password_form_fill_data.cc @@ -24,7 +24,7 @@ PasswordFormFillData::PasswordFormFillData() = default; PasswordFormFillData::PasswordFormFillData( const PasswordForm& form_on_page, - const std::map<base::string16, const PasswordForm*>& matches, + const std::vector<const PasswordForm*>& matches, const PasswordForm& preferred_match, bool wait_for_username) : form_renderer_id(form_on_page.form_data.unique_renderer_id), @@ -65,17 +65,25 @@ PasswordFormFillData::PasswordFormFillData( preferred_realm = preferred_match.signon_realm; // Copy additional username/value pairs. - for (const auto& it : matches) { - if (it.second != &preferred_match) { - PasswordAndRealm& value = additional_logins[it.first]; - value.password = it.second->password_value; - if (IsPublicSuffixMatchOrAffiliationBasedMatch(*it.second)) - value.realm = it.second->signon_realm; - } + for (const PasswordForm* match : matches) { + if (match == &preferred_match) + continue; + PasswordAndRealm& value = additional_logins[match->username_value]; + value.password = match->password_value; + if (IsPublicSuffixMatchOrAffiliationBasedMatch(*match)) + value.realm = match->signon_realm; } } -PasswordFormFillData::PasswordFormFillData(const PasswordFormFillData& other) = +PasswordFormFillData::PasswordFormFillData(const PasswordFormFillData&) = + default; + +PasswordFormFillData& PasswordFormFillData::operator=( + const PasswordFormFillData&) = default; + +PasswordFormFillData::PasswordFormFillData(PasswordFormFillData&&) = default; + +PasswordFormFillData& PasswordFormFillData::operator=(PasswordFormFillData&&) = default; PasswordFormFillData::~PasswordFormFillData() = default; diff --git a/chromium/components/autofill/core/common/password_form_fill_data.h b/chromium/components/autofill/core/common/password_form_fill_data.h index dfe6586ffbb..a94a1aeeb28 100644 --- a/chromium/components/autofill/core/common/password_form_fill_data.h +++ b/chromium/components/autofill/core/common/password_form_fill_data.h @@ -6,12 +6,21 @@ #define COMPONENTS_AUTOFILL_CORE_COMMON_PASSWORD_FORM_FILL_DATA_H_ #include <map> +#include <vector> #include "components/autofill/core/common/form_data.h" #include "components/autofill/core/common/password_form.h" namespace autofill { +// Contains renderer ids of password related elements found by the form parser. +struct ParsingResult { + uint32_t username_renderer_id; + uint32_t password_renderer_id; + uint32_t new_password_renderer_id; + uint32_t confirm_password_renderer_id; +}; + struct PasswordAndRealm { base::string16 password; std::string realm; @@ -32,14 +41,15 @@ struct PasswordFormFillData { // not autofill anything until the user typed in a valid username and blurred // the field. If |enable_possible_usernames| is true, we will populate // possible_usernames. - PasswordFormFillData( - const PasswordForm& form_on_page, - const std::map<base::string16, const PasswordForm*>& matches, - const PasswordForm& preferred_match, - bool wait_for_username); - - PasswordFormFillData(const PasswordFormFillData& other); - + PasswordFormFillData(const PasswordForm& form_on_page, + const std::vector<const PasswordForm*>& matches, + const PasswordForm& preferred_match, + bool wait_for_username); + + PasswordFormFillData(const PasswordFormFillData&); + PasswordFormFillData& operator=(const PasswordFormFillData&); + PasswordFormFillData(PasswordFormFillData&&); + PasswordFormFillData& operator=(PasswordFormFillData&&); ~PasswordFormFillData(); // If |has_renderer_ids| == true then |form_renderer_id| contains the unique @@ -89,7 +99,6 @@ struct PasswordFormFillData { // TODO(https://crbug.com/831123): Remove this field when old parsing is // removed and filling by renderer ids is by default. bool has_renderer_ids = false; - }; // If |data.wait_for_username| is set, the renderer does not need to receive diff --git a/chromium/components/autofill/core/common/password_form_fill_data_unittest.cc b/chromium/components/autofill/core/common/password_form_fill_data_unittest.cc index b794cc1115f..2c863d433dd 100644 --- a/chromium/components/autofill/core/common/password_form_fill_data_unittest.cc +++ b/chromium/components/autofill/core/common/password_form_fill_data_unittest.cc @@ -47,7 +47,7 @@ TEST(PasswordFormFillDataTest, TestSinglePreferredMatch) { preferred_match.preferred = true; preferred_match.scheme = PasswordForm::Scheme::kHtml; - std::map<base::string16, const PasswordForm*> matches; + std::vector<const PasswordForm*> matches; PasswordFormFillData result(form_on_page, matches, preferred_match, true); @@ -127,10 +127,8 @@ TEST(PasswordFormFillDataTest, TestPublicSuffixDomainMatching) { public_suffix_match.scheme = PasswordForm::Scheme::kHtml; // Add one exact match and one public suffix match. - std::map<base::string16, const PasswordForm*> matches; - matches.insert(std::make_pair(exact_match.username_value, &exact_match)); - matches.insert( - std::make_pair(public_suffix_match.username_value, &public_suffix_match)); + std::vector<const PasswordForm*> matches = {&exact_match, + &public_suffix_match}; PasswordFormFillData result(form_on_page, matches, preferred_match, true); EXPECT_TRUE(result.wait_for_username); @@ -202,10 +200,7 @@ TEST(PasswordFormFillDataTest, TestAffiliationMatch) { affiliated_match.scheme = PasswordForm::Scheme::kHtml; // Add one exact match and one affiliation based match. - std::map<base::string16, const PasswordForm*> matches; - matches.insert(std::make_pair(exact_match.username_value, &exact_match)); - matches.insert( - std::make_pair(affiliated_match.username_value, &affiliated_match)); + std::vector<const PasswordForm*> matches = {&exact_match, &affiliated_match}; PasswordFormFillData result(form_on_page, matches, preferred_match, false); EXPECT_FALSE(result.wait_for_username); @@ -249,9 +244,7 @@ TEST(PasswordFormFillDataTest, RendererIDs) { form_on_page.username_element_renderer_id = 123; form_on_page.password_element_renderer_id = 456; - std::map<base::string16, const PasswordForm*> matches; - - PasswordFormFillData result(form_on_page, matches, preferred_match, true); + PasswordFormFillData result(form_on_page, {}, preferred_match, true); EXPECT_EQ(form_data.unique_renderer_id, result.form_renderer_id); EXPECT_EQ(form_on_page.has_renderer_ids, result.has_renderer_ids); diff --git a/chromium/components/autofill/core/common/password_generation_util.cc b/chromium/components/autofill/core/common/password_generation_util.cc index 8bab183bf31..6f89ed2622d 100644 --- a/chromium/components/autofill/core/common/password_generation_util.cc +++ b/chromium/components/autofill/core/common/password_generation_util.cc @@ -17,12 +17,14 @@ PasswordGenerationUIData::PasswordGenerationUIData( int max_length, const base::string16& generation_element, uint32_t generation_element_id, + bool is_generation_element_password_type, base::i18n::TextDirection text_direction, const autofill::PasswordForm& password_form) : bounds(bounds), max_length(max_length), generation_element(generation_element), generation_element_id(generation_element_id), + is_generation_element_password_type(is_generation_element_password_type), text_direction(text_direction), password_form(password_form) {} diff --git a/chromium/components/autofill/core/common/password_generation_util.h b/chromium/components/autofill/core/common/password_generation_util.h index e3da088110b..c2143d13476 100644 --- a/chromium/components/autofill/core/common/password_generation_util.h +++ b/chromium/components/autofill/core/common/password_generation_util.h @@ -109,6 +109,7 @@ struct PasswordGenerationUIData { int max_length, const base::string16& generation_element, uint32_t generation_element_id, + bool is_generation_element_password_type, base::i18n::TextDirection text_direction, const autofill::PasswordForm& password_form); PasswordGenerationUIData(); @@ -132,6 +133,9 @@ struct PasswordGenerationUIData { // Renderer ID of the generation element. uint32_t generation_element_id; + // Is the generation element |type=password|. + bool is_generation_element_password_type; + // Direction of the text for |generation_element|. base::i18n::TextDirection text_direction; @@ -141,10 +145,6 @@ struct PasswordGenerationUIData { void LogPasswordGenerationEvent(PasswordGenerationEvent event); -// Returns true if Password Generation is enabled according to the field -// trial result and the flags. -bool IsPasswordGenerationEnabled(); - } // namespace password_generation } // namespace autofill diff --git a/chromium/components/autofill/core/common/save_password_progress_logger.cc b/chromium/components/autofill/core/common/save_password_progress_logger.cc index 91807e55dce..b4bd692a753 100644 --- a/chromium/components/autofill/core/common/save_password_progress_logger.cc +++ b/chromium/components/autofill/core/common/save_password_progress_logger.cc @@ -275,16 +275,8 @@ std::string SavePasswordProgressLogger::GetStringFromID( return "provisionally_saved_forms_[form_frame]"; case SavePasswordProgressLogger::STRING_PROVISIONALLY_SAVE_FORM_METHOD: return "PasswordManager::ProvisionallySaveForm"; - case SavePasswordProgressLogger::STRING_IS_SAVING_ENABLED: - return "IsSavingAndFillingEnabled"; case SavePasswordProgressLogger::STRING_EMPTY_PASSWORD: return "Empty password"; - case SavePasswordProgressLogger::STRING_EXACT_MATCH: - return "Form manager found, exact match"; - case SavePasswordProgressLogger::STRING_MATCH_WITHOUT_ACTION: - return "Form manager found, match except for action"; - case SavePasswordProgressLogger::STRING_ORIGINS_MATCH: - return "Form manager found, only origins match"; case SavePasswordProgressLogger::STRING_MATCHING_NOT_COMPLETE: return "No form manager has completed matching"; case SavePasswordProgressLogger::STRING_FORM_BLACKLISTED: @@ -295,8 +287,6 @@ std::string SavePasswordProgressLogger::GetStringFromID( return "Credential is used for syncing passwords"; case STRING_BLOCK_PASSWORD_SAME_ORIGIN_INSECURE_SCHEME: return "Blocked password due to same origin but insecure scheme"; - case SavePasswordProgressLogger::STRING_PROVISIONALLY_SAVED_FORM: - return "provisionally_saved_form"; case SavePasswordProgressLogger::STRING_ON_PASSWORD_FORMS_RENDERED_METHOD: return "PasswordManager::OnPasswordFormsRendered"; case SavePasswordProgressLogger::STRING_ON_SAME_DOCUMENT_NAVIGATION: @@ -323,8 +313,6 @@ std::string SavePasswordProgressLogger::GetStringFromID( return "Show password prompt"; case SavePasswordProgressLogger::STRING_PASSWORDMANAGER_AUTOFILL: return "PasswordManager::Autofill"; - case SavePasswordProgressLogger::STRING_PASSWORDMANAGER_AUTOFILLHTTPAUTH: - return "PasswordManager::AutofillHttpAuth"; case SavePasswordProgressLogger:: STRING_PASSWORDMANAGER_SHOW_INITIAL_PASSWORD_ACCOUNT_SUGGESTIONS: return "PasswordManager::ShowInitialPasswordAccountSuggestions"; @@ -338,10 +326,6 @@ std::string SavePasswordProgressLogger::GetStringFromID( case SavePasswordProgressLogger:: STRING_PROVISIONALLY_SAVED_FORM_IS_NOT_HTML: return "Provisionally saved form is not HTML"; - case SavePasswordProgressLogger::STRING_ON_FETCH_COMPLETED_METHOD: - return "PasswordFormManager::OnFetchCompleted"; - case SavePasswordProgressLogger::STRING_BEST_SCORE: - return "best_score"; case SavePasswordProgressLogger::STRING_ON_GET_STORE_RESULTS_METHOD: return "FormFetcherImpl::OnGetPasswordStoreResults"; case SavePasswordProgressLogger::STRING_NUMBER_RESULTS: @@ -352,8 +336,6 @@ std::string SavePasswordProgressLogger::GetStringFromID( return "PasswordStore is not available"; case SavePasswordProgressLogger::STRING_CREATE_LOGIN_MANAGERS_METHOD: return "PasswordManager::CreatePendingLoginManagers"; - case SavePasswordProgressLogger::STRING_NEW_NUMBER_LOGIN_MANAGERS: - return "Number of pending login managers (after)"; case SavePasswordProgressLogger:: STRING_PASSWORD_MANAGEMENT_ENABLED_FOR_CURRENT_PAGE: return "IsPasswordManagementEnabledForCurrentPage"; @@ -361,12 +343,6 @@ std::string SavePasswordProgressLogger::GetStringFromID( return "ShowLoginPrompt"; case SavePasswordProgressLogger::STRING_NEW_UI_STATE: return "The new state of the UI"; - case SavePasswordProgressLogger::STRING_FORM_NOT_AUTOFILLED: - return "The observed form will not be autofilled"; - case SavePasswordProgressLogger::STRING_CHANGE_PASSWORD_FORM: - return "Not saving password for a change password form"; - case SavePasswordProgressLogger::STRING_PROCESS_FRAME_METHOD: - return "PasswordFormManager::ProcessFrame"; case SavePasswordProgressLogger::STRING_FORM_SIGNATURE: return "Signature of form"; case SavePasswordProgressLogger::STRING_FORM_FETCHER_STATE: @@ -405,8 +381,6 @@ std::string SavePasswordProgressLogger::GetStringFromID( return "Form name"; case SavePasswordProgressLogger::STRING_FIELDS: return "Form fields"; - case SavePasswordProgressLogger::STRING_FORM_VOTES: - return "Form votes"; case SavePasswordProgressLogger::STRING_FIRSTUSE_FORM_VOTE: return "FirstUse vote"; case SavePasswordProgressLogger::STRING_PASSWORD_FORM_VOTE: @@ -417,26 +391,6 @@ std::string SavePasswordProgressLogger::GetStringFromID( return "Generation disabled: saving disabled"; case SavePasswordProgressLogger::STRING_GENERATION_DISABLED_NO_SYNC: return "Generation disabled: no sync"; - case STRING_GENERATION_RENDERER_INVALID_PASSWORD_FORM: - return "Generation invalid PasswordForm"; - case STRING_GENERATION_RENDERER_POSSIBLE_ACCOUNT_CREATION_FORMS: - return "Generation possible account creation forms"; - case STRING_GENERATION_RENDERER_NO_PASSWORD_MANAGER_ACCESS: - return "Generation: no PasswordManager access"; - case STRING_GENERATION_RENDERER_FORM_ALREADY_FOUND: - return "Generation: account creation form already found"; - case STRING_GENERATION_RENDERER_NO_POSSIBLE_CREATION_FORMS: - return "Generation: no possible account creation forms"; - case STRING_GENERATION_RENDERER_NOT_BLACKLISTED: - return "Generation: no non-blacklisted confirmation"; - case STRING_GENERATION_RENDERER_AUTOCOMPLETE_ATTRIBUTE: - return "Generation: autocomplete attributes found"; - case STRING_GENERATION_RENDERER_NO_SERVER_SIGNAL: - return "Generation: no server signal"; - case STRING_GENERATION_RENDERER_ELIGIBLE_FORM_FOUND: - return "Generation: eligible form found"; - case STRING_GENERATION_RENDERER_NO_FIELD_FOUND: - return "Generation: fields for generation are not found"; case STRING_GENERATION_RENDERER_AUTOMATIC_GENERATION_AVAILABLE: return "Generation: automatic generation is available"; case STRING_GENERATION_RENDERER_SHOW_MANUAL_GENERATION_POPUP: @@ -463,8 +417,6 @@ std::string SavePasswordProgressLogger::GetStringFromID( "exists whose username matches the prefilled value"; case STRING_FAILED_TO_FILL_FOUND_NO_PASSWORD_FOR_USERNAME: return "Failed to fill: No credential matching found"; - case SavePasswordProgressLogger::STRING_HTTPAUTH_OBSERVER_PRESENT: - return "Instances of HttpAuthObserver are present"; case SavePasswordProgressLogger:: STRING_HTTPAUTH_ON_ASK_USER_OR_SAVE_PASSWORD: return "HttpAuthManager::AskUserOrSavePassword"; @@ -476,26 +428,22 @@ std::string SavePasswordProgressLogger::GetStringFromID( return "HttpAuthManager::DetachObserver"; case SavePasswordProgressLogger::STRING_SHOW_ONBOARDING: return "Show onboarding experience and offer to save password"; + case STRING_LEAK_DETECTION_DISABLED_FEATURE: + return "Leak detection disabled in settings"; + case STRING_LEAK_DETECTION_DISABLED_SAFE_BROWSING: + return "Leak detection is off as the safe browsing is disabled"; case STRING_LEAK_DETECTION_FINISHED: return "Leak detection finished with result"; case STRING_LEAK_DETECTION_SIGNED_OUT_ERROR: return "Leak detection failed: signed out"; case STRING_LEAK_DETECTION_TOKEN_REQUEST_ERROR: return "Leak detection failed: can't get a token"; - case STRING_LEAK_DETECTION_ENCRYPTION_ERROR: - return "Leak detection failed: encryption"; case STRING_LEAK_DETECTION_INVALID_SERVER_RESPONSE_ERROR: return "Leak detection failed: invalid server response"; case SavePasswordProgressLogger:: STRING_PASSWORD_REQUIREMENTS_VOTE_FOR_LOWERCASE: return "Uploading password requirements vote for using lowercase letters"; case SavePasswordProgressLogger:: - STRING_PASSWORD_REQUIREMENTS_VOTE_FOR_UPPERCASE: - return "Uploading password requirements vote for using lowercase letters"; - case SavePasswordProgressLogger:: - STRING_PASSWORD_REQUIREMENTS_VOTE_FOR_NUMERICS: - return "Uploading password requirements vote for using numbers"; - case SavePasswordProgressLogger:: STRING_PASSWORD_REQUIREMENTS_VOTE_FOR_SPECIAL_SYMBOL: return "Uploading password requirements vote for using special symbols"; case SavePasswordProgressLogger:: @@ -504,9 +452,6 @@ std::string SavePasswordProgressLogger::GetStringFromID( case SavePasswordProgressLogger:: STRING_PASSWORD_REQUIREMENTS_VOTE_FOR_PASSWORD_LENGTH: return "Uploading password requirements vote for password length"; - case SavePasswordProgressLogger:: - STRING_PASSWORD_REQUIREMENTS_VOTE_NO_PASSWORD_ATTRIBUTES: - return "No password requirements attributed set"; case STRING_SAVE_PASSWORD_HASH: return "Password hash is saved"; case STRING_DID_NAVIGATE_MAIN_FRAME: @@ -515,6 +460,8 @@ std::string SavePasswordProgressLogger::GetStringFromID( return "Navigation to New Tab page"; case STRING_SERVER_PREDICTIONS: return "Server predictions"; + case STRING_USERNAME_FIRST_FLOW_VOTE: + return "Username first flow vote"; case SavePasswordProgressLogger::STRING_INVALID: return "INVALID"; // Intentionally no default: clause here -- all IDs need to get covered. diff --git a/chromium/components/autofill/core/common/save_password_progress_logger.h b/chromium/components/autofill/core/common/save_password_progress_logger.h index f1899def840..f6f4a681b76 100644 --- a/chromium/components/autofill/core/common/save_password_progress_logger.h +++ b/chromium/components/autofill/core/common/save_password_progress_logger.h @@ -78,17 +78,12 @@ class SavePasswordProgressLogger { STRING_FRAME_NOT_MAIN_FRAME, STRING_PROVISIONALLY_SAVED_FORM_FOR_FRAME, STRING_PROVISIONALLY_SAVE_FORM_METHOD, - STRING_IS_SAVING_ENABLED, STRING_EMPTY_PASSWORD, - STRING_EXACT_MATCH, - STRING_MATCH_WITHOUT_ACTION, - STRING_ORIGINS_MATCH, STRING_MATCHING_NOT_COMPLETE, STRING_FORM_BLACKLISTED, STRING_INVALID_FORM, STRING_SYNC_CREDENTIAL, STRING_BLOCK_PASSWORD_SAME_ORIGIN_INSECURE_SCHEME, - STRING_PROVISIONALLY_SAVED_FORM, STRING_ON_PASSWORD_FORMS_RENDERED_METHOD, STRING_ON_SAME_DOCUMENT_NAVIGATION, STRING_ON_ASK_USER_OR_SAVE_PASSWORD, @@ -102,26 +97,19 @@ class SavePasswordProgressLogger { STRING_ONLY_VISIBLE, STRING_SHOW_PASSWORD_PROMPT, STRING_PASSWORDMANAGER_AUTOFILL, - STRING_PASSWORDMANAGER_AUTOFILLHTTPAUTH, STRING_PASSWORDMANAGER_SHOW_INITIAL_PASSWORD_ACCOUNT_SUGGESTIONS, STRING_WAIT_FOR_USERNAME, STRING_WAS_LAST_NAVIGATION_HTTP_ERROR_METHOD, STRING_HTTP_STATUS_CODE, STRING_PROVISIONALLY_SAVED_FORM_IS_NOT_HTML, - STRING_ON_FETCH_COMPLETED_METHOD, - STRING_BEST_SCORE, STRING_ON_GET_STORE_RESULTS_METHOD, STRING_NUMBER_RESULTS, STRING_FETCH_METHOD, STRING_NO_STORE, STRING_CREATE_LOGIN_MANAGERS_METHOD, - STRING_NEW_NUMBER_LOGIN_MANAGERS, STRING_PASSWORD_MANAGEMENT_ENABLED_FOR_CURRENT_PAGE, STRING_SHOW_LOGIN_PROMPT_METHOD, STRING_NEW_UI_STATE, - STRING_FORM_NOT_AUTOFILLED, - STRING_CHANGE_PASSWORD_FORM, - STRING_PROCESS_FRAME_METHOD, STRING_FORM_SIGNATURE, STRING_FORM_FETCHER_STATE, STRING_UNOWNED_INPUTS_VISIBLE, @@ -140,22 +128,11 @@ class SavePasswordProgressLogger { STRING_PASSWORD_FILLED, STRING_FORM_NAME, STRING_FIELDS, - STRING_FORM_VOTES, STRING_FIRSTUSE_FORM_VOTE, STRING_PASSWORD_FORM_VOTE, STRING_REUSE_FOUND, STRING_GENERATION_DISABLED_SAVING_DISABLED, STRING_GENERATION_DISABLED_NO_SYNC, - STRING_GENERATION_RENDERER_INVALID_PASSWORD_FORM, - STRING_GENERATION_RENDERER_POSSIBLE_ACCOUNT_CREATION_FORMS, - STRING_GENERATION_RENDERER_NO_PASSWORD_MANAGER_ACCESS, - STRING_GENERATION_RENDERER_FORM_ALREADY_FOUND, - STRING_GENERATION_RENDERER_NO_POSSIBLE_CREATION_FORMS, - STRING_GENERATION_RENDERER_NOT_BLACKLISTED, - STRING_GENERATION_RENDERER_AUTOCOMPLETE_ATTRIBUTE, - STRING_GENERATION_RENDERER_NO_SERVER_SIGNAL, - STRING_GENERATION_RENDERER_ELIGIBLE_FORM_FOUND, - STRING_GENERATION_RENDERER_NO_FIELD_FOUND, STRING_GENERATION_RENDERER_AUTOMATIC_GENERATION_AVAILABLE, STRING_GENERATION_RENDERER_SHOW_MANUAL_GENERATION_POPUP, STRING_GENERATION_RENDERER_GENERATED_PASSWORD_ACCEPTED, @@ -168,28 +145,26 @@ class SavePasswordProgressLogger { STRING_FAILED_TO_FILL_NO_AUTOCOMPLETEABLE_ELEMENT, STRING_FAILED_TO_FILL_PREFILLED_USERNAME, STRING_FAILED_TO_FILL_FOUND_NO_PASSWORD_FOR_USERNAME, - STRING_HTTPAUTH_OBSERVER_PRESENT, STRING_HTTPAUTH_ON_ASK_USER_OR_SAVE_PASSWORD, STRING_HTTPAUTH_ON_PROMPT_USER, STRING_HTTPAUTH_ON_SET_OBSERVER, STRING_HTTPAUTH_ON_DETACH_OBSERVER, STRING_SHOW_ONBOARDING, + STRING_LEAK_DETECTION_DISABLED_FEATURE, + STRING_LEAK_DETECTION_DISABLED_SAFE_BROWSING, STRING_LEAK_DETECTION_FINISHED, STRING_LEAK_DETECTION_SIGNED_OUT_ERROR, STRING_LEAK_DETECTION_TOKEN_REQUEST_ERROR, - STRING_LEAK_DETECTION_ENCRYPTION_ERROR, STRING_LEAK_DETECTION_INVALID_SERVER_RESPONSE_ERROR, STRING_PASSWORD_REQUIREMENTS_VOTE_FOR_LOWERCASE, - STRING_PASSWORD_REQUIREMENTS_VOTE_FOR_UPPERCASE, - STRING_PASSWORD_REQUIREMENTS_VOTE_FOR_NUMERICS, STRING_PASSWORD_REQUIREMENTS_VOTE_FOR_SPECIAL_SYMBOL, STRING_PASSWORD_REQUIREMENTS_VOTE_FOR_SPECIFIC_SPECIAL_SYMBOL, STRING_PASSWORD_REQUIREMENTS_VOTE_FOR_PASSWORD_LENGTH, - STRING_PASSWORD_REQUIREMENTS_VOTE_NO_PASSWORD_ATTRIBUTES, STRING_SAVE_PASSWORD_HASH, STRING_DID_NAVIGATE_MAIN_FRAME, STRING_NAVIGATION_NTP, STRING_SERVER_PREDICTIONS, + STRING_USERNAME_FIRST_FLOW_VOTE, STRING_INVALID, // Represents a string returned in a case of an error. STRING_MAX = STRING_INVALID }; diff --git a/chromium/components/autofill/ios/browser/autofill_agent.mm b/chromium/components/autofill/ios/browser/autofill_agent.mm index 092c7847147..c10e6115cbf 100644 --- a/chromium/components/autofill/ios/browser/autofill_agent.mm +++ b/chromium/components/autofill/ios/browser/autofill_agent.mm @@ -53,7 +53,7 @@ #import "ios/web/public/js_messaging/web_frames_manager.h" #import "ios/web/public/navigation/navigation_context.h" #import "ios/web/public/web_state.h" -#import "ios/web/public/web_state/web_state_observer_bridge.h" +#import "ios/web/public/web_state_observer_bridge.h" #include "ui/gfx/geometry/rect.h" #include "url/gurl.h" @@ -503,7 +503,6 @@ autofillManagerFromWebState:(web::WebState*)webState popupDelegate: (const base::WeakPtr<autofill::AutofillPopupDelegate>&) delegate { - bool has_gpay_branding = false; // Convert the suggestions into an NSArray for the keyboard. NSMutableArray<FormSuggestion*>* suggestions = [[NSMutableArray alloc] init]; for (auto popup_suggestion : popup_suggestions) { @@ -514,8 +513,7 @@ autofillManagerFromWebState:(web::WebState*)webState // fortunately almost all the entries we are interested in (profile or // autofill entries) are zero or positive. Negative entries we are // interested in is autofill::POPUP_ITEM_ID_CLEAR_FORM, used to show the - // "clear form" button and autofill::POPUP_ITEM_ID_GOOGLE_PAY_BRANDING, used - // to show the "Google Pay" branding. + // "clear form" button. NSString* value = nil; NSString* displayDescription = nil; if (popup_suggestion.frontend_id >= 0) { @@ -535,11 +533,6 @@ autofillManagerFromWebState:(web::WebState*)webState // Show the "clear form" button. value = base::SysUTF16ToNSString(popup_suggestion.value); } else if (popup_suggestion.frontend_id == - autofill::POPUP_ITEM_ID_GOOGLE_PAY_BRANDING) { - // Show "GPay branding" icon - value = base::SysUTF16ToNSString(popup_suggestion.value); - has_gpay_branding = true; - } else if (popup_suggestion.frontend_id == autofill::POPUP_ITEM_ID_SHOW_ACCOUNT_CARDS) { // Show opt-in for showing cards from account. value = base::SysUTF16ToNSString(popup_suggestion.value); @@ -554,10 +547,9 @@ autofillManagerFromWebState:(web::WebState*)webState icon:base::SysUTF8ToNSString(popup_suggestion.icon) identifier:popup_suggestion.frontend_id]; - // Put "clear form" entry at the front of the suggestions. If - // "GPay branding" icon is present, it remains as the first suggestion. + // Put "clear form" entry at the front of the suggestions. if (popup_suggestion.frontend_id == autofill::POPUP_ITEM_ID_CLEAR_FORM) { - [suggestions insertObject:suggestion atIndex:has_gpay_branding ? 1 : 0]; + [suggestions insertObject:suggestion atIndex:0]; } else { [suggestions addObject:suggestion]; } diff --git a/chromium/components/autofill/ios/browser/autofill_agent_unittests.mm b/chromium/components/autofill/ios/browser/autofill_agent_unittests.mm index 7a83d99dfce..aee0fe52e81 100644 --- a/chromium/components/autofill/ios/browser/autofill_agent_unittests.mm +++ b/chromium/components/autofill/ios/browser/autofill_agent_unittests.mm @@ -36,7 +36,6 @@ #endif using autofill::POPUP_ITEM_ID_CLEAR_FORM; -using autofill::POPUP_ITEM_ID_GOOGLE_PAY_BRANDING; using autofill::POPUP_ITEM_ID_SHOW_ACCOUNT_CARDS; using base::test::ios::WaitUntilCondition; @@ -379,8 +378,6 @@ TEST_F(AutofillAgentTests, onSuggestionsReady_ClearFormWithGPay) { // Make the suggestions available to AutofillAgent. std::vector<autofill::Suggestion> suggestions; - suggestions.push_back( - autofill::Suggestion("", "", "", POPUP_ITEM_ID_GOOGLE_PAY_BRANDING)); suggestions.push_back(autofill::Suggestion("", "", "", 123)); suggestions.push_back(autofill::Suggestion("", "", "", 321)); suggestions.push_back( @@ -410,15 +407,11 @@ TEST_F(AutofillAgentTests, onSuggestionsReady_ClearFormWithGPay) { return completion_handler_called; }); - // GPay icon should appear as the first suggestion followed by "Clear Form". - // Otherwise, the order of suggestions should not change. - EXPECT_EQ(4U, completion_handler_suggestions.count); - EXPECT_EQ(POPUP_ITEM_ID_GOOGLE_PAY_BRANDING, - completion_handler_suggestions[0].identifier); + EXPECT_EQ(3U, completion_handler_suggestions.count); EXPECT_EQ(POPUP_ITEM_ID_CLEAR_FORM, - completion_handler_suggestions[1].identifier); - EXPECT_EQ(123, completion_handler_suggestions[2].identifier); - EXPECT_EQ(321, completion_handler_suggestions[3].identifier); + completion_handler_suggestions[0].identifier); + EXPECT_EQ(123, completion_handler_suggestions[1].identifier); + EXPECT_EQ(321, completion_handler_suggestions[2].identifier); } // Test that every frames are processed whatever is the order of pageloading diff --git a/chromium/components/autofill/ios/browser/autofill_driver_ios.h b/chromium/components/autofill/ios/browser/autofill_driver_ios.h index 9783f364b46..08226a74a59 100644 --- a/chromium/components/autofill/ios/browser/autofill_driver_ios.h +++ b/chromium/components/autofill/ios/browser/autofill_driver_ios.h @@ -60,7 +60,8 @@ class AutofillDriverIOS : public AutofillDriver { void RendererShouldFillFieldWithValue(const base::string16& value) override; void RendererShouldPreviewFieldWithValue( const base::string16& value) override; - void RendererShouldSetSuggestionAvailability(bool available) override; + void RendererShouldSetSuggestionAvailability( + const mojom::AutofillState state) override; void PopupHidden() override; gfx::RectF TransformBoundingBoxToViewportCoordinates( const gfx::RectF& bounding_box) override; diff --git a/chromium/components/autofill/ios/browser/autofill_driver_ios.mm b/chromium/components/autofill/ios/browser/autofill_driver_ios.mm index 7c18d5f3b6d..c9bce04f629 100644 --- a/chromium/components/autofill/ios/browser/autofill_driver_ios.mm +++ b/chromium/components/autofill/ios/browser/autofill_driver_ios.mm @@ -131,7 +131,7 @@ void AutofillDriverIOS::RendererShouldPreviewFieldWithValue( } void AutofillDriverIOS::RendererShouldSetSuggestionAvailability( - bool available) {} + const mojom::AutofillState state) {} void AutofillDriverIOS::PopupHidden() { } diff --git a/chromium/components/autofill/ios/browser/autofill_driver_ios_webframe.h b/chromium/components/autofill/ios/browser/autofill_driver_ios_webframe.h index 6e1c543769b..99e6f83f7bc 100644 --- a/chromium/components/autofill/ios/browser/autofill_driver_ios_webframe.h +++ b/chromium/components/autofill/ios/browser/autofill_driver_ios_webframe.h @@ -7,7 +7,7 @@ #include "components/autofill/ios/browser/autofill_driver_ios.h" #include "ios/web/public/js_messaging/web_frame_user_data.h" -#include "ios/web/public/web_state/web_state_user_data.h" +#import "ios/web/public/web_state_user_data.h" namespace web { class WebFrame; diff --git a/chromium/components/autofill/ios/browser/credit_card_save_manager_test_observer_bridge.h b/chromium/components/autofill/ios/browser/credit_card_save_manager_test_observer_bridge.h index 6c8f2d6ec8a..85a66449c0f 100644 --- a/chromium/components/autofill/ios/browser/credit_card_save_manager_test_observer_bridge.h +++ b/chromium/components/autofill/ios/browser/credit_card_save_manager_test_observer_bridge.h @@ -38,7 +38,7 @@ class CreditCardSaveManagerTestObserverBridge explicit CreditCardSaveManagerTestObserverBridge( CreditCardSaveManager* credit_card_save_manager, id<CreditCardSaveManagerTestObserver> observer); - virtual ~CreditCardSaveManagerTestObserverBridge() = default; + ~CreditCardSaveManagerTestObserverBridge() override = default; // CreditCardSaveManager::ObserverForTest: void OnOfferLocalSave() override; diff --git a/chromium/components/autofill/ios/browser/js_autofill_manager.mm b/chromium/components/autofill/ios/browser/js_autofill_manager.mm index c81f80ff95a..2b134fcfc73 100644 --- a/chromium/components/autofill/ios/browser/js_autofill_manager.mm +++ b/chromium/components/autofill/ios/browser/js_autofill_manager.mm @@ -28,7 +28,7 @@ @implementation JsAutofillManager { // The injection receiver used to evaluate JavaScript. - CRWJSInjectionReceiver* _receiver; + __weak CRWJSInjectionReceiver* _receiver; } - (instancetype)initWithReceiver:(CRWJSInjectionReceiver*)receiver { diff --git a/chromium/components/autofill/ios/browser/js_suggestion_manager.mm b/chromium/components/autofill/ios/browser/js_suggestion_manager.mm index 07a81ca2d42..d7fd9a123e6 100644 --- a/chromium/components/autofill/ios/browser/js_suggestion_manager.mm +++ b/chromium/components/autofill/ios/browser/js_suggestion_manager.mm @@ -24,7 +24,7 @@ @implementation JsSuggestionManager { // The injection receiver used to evaluate JavaScript. - CRWJSInjectionReceiver* _receiver; + __weak CRWJSInjectionReceiver* _receiver; web::WebFramesManager* _webFramesManager; } diff --git a/chromium/components/autofill/ios/form_util/form_activity_tab_helper.h b/chromium/components/autofill/ios/form_util/form_activity_tab_helper.h index dc40fa6199f..976a4634857 100644 --- a/chromium/components/autofill/ios/form_util/form_activity_tab_helper.h +++ b/chromium/components/autofill/ios/form_util/form_activity_tab_helper.h @@ -8,8 +8,8 @@ #include "base/macros.h" #include "base/observer_list.h" #include "base/values.h" -#include "ios/web/public/web_state/web_state_observer.h" -#import "ios/web/public/web_state/web_state_user_data.h" +#include "ios/web/public/web_state_observer.h" +#import "ios/web/public/web_state_user_data.h" namespace autofill { |