diff options
Diffstat (limited to 'chromium/components/autofill')
135 files changed, 5061 insertions, 1873 deletions
diff --git a/chromium/components/autofill/DEPS b/chromium/components/autofill/DEPS index bed4f0afd3c..6b6ca5cf0be 100644 --- a/chromium/components/autofill/DEPS +++ b/chromium/components/autofill/DEPS @@ -4,9 +4,14 @@ include_rules = [ "+grit", # For generated headers "+jni", "+net", + "+third_party/skia", "+third_party/zlib/google", "+ui", + # Autofill is a layered component; subdirectories must explicitly introduce # the ability to use the content layer as appropriate. "-components/autofill/content", + + # This directory contains build flags and does not pull all of PPAPI in. + "+ppapi/features", ] diff --git a/chromium/components/autofill/OWNERS b/chromium/components/autofill/OWNERS index be52fd5be44..d52021494e9 100644 --- a/chromium/components/autofill/OWNERS +++ b/chromium/components/autofill/OWNERS @@ -1,6 +1,6 @@ estade@chromium.org -isherman@chromium.org mathp@chromium.org +sebsg@chromium.org vabr@chromium.org # Owners for password autofill/generation only. diff --git a/chromium/components/autofill/android/BUILD.gn b/chromium/components/autofill/android/BUILD.gn index 54889a8605d..dab9ec22618 100644 --- a/chromium/components/autofill/android/BUILD.gn +++ b/chromium/components/autofill/android/BUILD.gn @@ -13,6 +13,7 @@ android_library("autofill_java") { deps = [ ":autofill_java_resources", "//base:base_java", + "//third_party/android_tools:android_support_v7_appcompat_java", "//ui/android:ui_java", ] java_files = [ diff --git a/chromium/components/autofill/android/java/res/values/colors.xml b/chromium/components/autofill/android/java/res/values/colors.xml new file mode 100644 index 00000000000..8b10dd3af7b --- /dev/null +++ b/chromium/components/autofill/android/java/res/values/colors.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2016 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. +--> +<resources> + <!-- Text colors of warning messages for credit card and password forms. --> + <color name="http_bad_warning_message_text">#C53929</color> + <color name="insecure_context_payment_disabled_message_text">#646464</color> +</resources> diff --git a/chromium/components/autofill/android/java/res/values/dimens.xml b/chromium/components/autofill/android/java/res/values/dimens.xml index ee949e47a0f..82d8c5e07c2 100644 --- a/chromium/components/autofill/android/java/res/values/dimens.xml +++ b/chromium/components/autofill/android/java/res/values/dimens.xml @@ -15,4 +15,13 @@ <dimen name="keyboard_accessory_height">48dp</dimen> <dimen name="keyboard_accessory_padding">8dp</dimen> <dimen name="keyboard_accessory_text_size">14sp</dimen> + + <!-- + Larger label and icon sizes for Form-Not-Secure experiment + (warning messages on credit card and password forms when users + are on HTTP sites). + --> + <dimen name="dropdown_item_larger_sublabel_font_size">18sp</dimen> + <dimen name="dropdown_large_icon_size">24dp</dimen> + <dimen name="dropdown_large_icon_margin">10dp</dimen> </resources> diff --git a/chromium/components/autofill/content/browser/BUILD.gn b/chromium/components/autofill/content/browser/BUILD.gn index d8819209383..88576886284 100644 --- a/chromium/components/autofill/content/browser/BUILD.gn +++ b/chromium/components/autofill/content/browser/BUILD.gn @@ -37,6 +37,7 @@ static_library("browser") { "//gpu/config", "//mojo/common:common_base", "//net", + "//ppapi/features", "//services/service_manager/public/cpp", "//sql", "//third_party/icu", diff --git a/chromium/components/autofill/content/browser/content_autofill_driver.cc b/chromium/components/autofill/content/browser/content_autofill_driver.cc index c0648785994..8eb8de071ae 100644 --- a/chromium/components/autofill/content/browser/content_autofill_driver.cc +++ b/chromium/components/autofill/content/browser/content_autofill_driver.cc @@ -159,15 +159,15 @@ void ContentAutofillDriver::PopupHidden() { gfx::RectF ContentAutofillDriver::TransformBoundingBoxToViewportCoordinates( const gfx::RectF& bounding_box) { - gfx::Point orig_point(bounding_box.x(), bounding_box.y()); - gfx::Point transformed_point; - transformed_point = - render_frame_host_->GetView()->TransformPointToRootCoordSpace(orig_point); + content::RenderWidgetHostView* view = render_frame_host_->GetView(); + if (!view) + return bounding_box; - gfx::RectF new_box; - new_box.SetRect(transformed_point.x(), transformed_point.y(), - bounding_box.width(), bounding_box.height()); - return new_box; + gfx::Point orig_point(bounding_box.x(), bounding_box.y()); + gfx::Point transformed_point = + view->TransformPointToRootCoordSpace(orig_point); + return gfx::RectF(transformed_point.x(), transformed_point.y(), + bounding_box.width(), bounding_box.height()); } void ContentAutofillDriver::DidInteractWithCreditCardForm() { @@ -216,10 +216,6 @@ void ContentAutofillDriver::HidePopup() { autofill_manager_->OnHidePopup(); } -void ContentAutofillDriver::PingAck() { - autofill_external_delegate_.OnPingAck(); -} - void ContentAutofillDriver::FocusNoLongerOnForm() { autofill_manager_->OnFocusNoLongerOnForm(); } @@ -264,7 +260,7 @@ const mojom::AutofillAgentPtr& ContentAutofillDriver::GetAutofillAgent() { // Here is a lazy binding, and will not reconnect after connection error. if (!autofill_agent_) { render_frame_host_->GetRemoteInterfaces()->GetInterface( - mojo::GetProxy(&autofill_agent_)); + mojo::MakeRequest(&autofill_agent_)); } return autofill_agent_; diff --git a/chromium/components/autofill/content/browser/content_autofill_driver.h b/chromium/components/autofill/content/browser/content_autofill_driver.h index e5740f57624..2fd226efc69 100644 --- a/chromium/components/autofill/content/browser/content_autofill_driver.h +++ b/chromium/components/autofill/content/browser/content_autofill_driver.h @@ -17,7 +17,6 @@ #include "mojo/public/cpp/bindings/binding.h" namespace content { -class BrowserContext; class RenderFrameHost; struct FrameNavigateParams; struct LoadCommittedDetails; @@ -84,7 +83,6 @@ class ContentAutofillDriver : public AutofillDriver, const FormFieldData& field, const gfx::RectF& bounding_box) override; void HidePopup() override; - void PingAck() override; void FocusNoLongerOnForm() override; void DidFillAutofillFormData(const FormData& form, base::TimeTicks timestamp) override; 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 8bec3e5a051..2781ee4c1cf 100644 --- a/chromium/components/autofill/content/browser/content_autofill_driver_factory.h +++ b/chromium/components/autofill/content/browser/content_autofill_driver_factory.h @@ -20,7 +20,6 @@ class RenderFrameHost; namespace autofill { -class AutofillDriver; class ContentAutofillDriver; // Manages lifetime of ContentAutofillDriver. One Factory per WebContents 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 82dd2f6254a..3dbbd572f3c 100644 --- a/chromium/components/autofill/content/browser/content_autofill_driver_unittest.cc +++ b/chromium/components/autofill/content/browser/content_autofill_driver_unittest.cc @@ -137,6 +137,9 @@ class FakeAutofillAgent : public mojom::AutofillAgent { return true; } + // Mocked mojom::AutofillAgent methods: + MOCK_METHOD0(FirstUserGestureObservedInTab, void()); + private: void CallDone() { if (!quit_closure_.is_null()) { @@ -145,9 +148,7 @@ class FakeAutofillAgent : public mojom::AutofillAgent { } } - // mojom::AutofillAgent methods: - void FirstUserGestureObservedInTab() override {} - + // mojom::AutofillAgent: void FillForm(int32_t id, const FormData& form) override { fill_form_id_ = id; fill_form_form_ = form; @@ -236,6 +237,11 @@ class MockAutofillManager : public AutofillManager { MOCK_METHOD0(Reset, void()); }; +class MockAutofillClient : public TestAutofillClient { + public: + MOCK_METHOD0(OnFirstUserGestureObserved, void()); +}; + class TestContentAutofillDriver : public ContentAutofillDriver { public: TestContentAutofillDriver(content::RenderFrameHost* rfh, @@ -259,7 +265,7 @@ class ContentAutofillDriverTest : public content::RenderViewHostTestHarness { void SetUp() override { content::RenderViewHostTestHarness::SetUp(); - test_autofill_client_.reset(new TestAutofillClient()); + test_autofill_client_.reset(new MockAutofillClient()); driver_.reset(new TestContentAutofillDriver(web_contents()->GetMainFrame(), test_autofill_client_.get())); @@ -279,7 +285,7 @@ class ContentAutofillDriverTest : public content::RenderViewHostTestHarness { } protected: - std::unique_ptr<TestAutofillClient> test_autofill_client_; + std::unique_ptr<MockAutofillClient> test_autofill_client_; std::unique_ptr<TestContentAutofillDriver> driver_; FakeAutofillAgent fake_agent_; @@ -479,4 +485,15 @@ TEST_F(ContentAutofillDriverTest, CreditCardFormInteractionOnHTTPS) { content::SSLStatus::DISPLAYED_CREDIT_CARD_FIELD_ON_HTTP)); } +TEST_F(ContentAutofillDriverTest, NotifyFirstUserGestureObservedInTab) { + driver_->NotifyFirstUserGestureObservedInTab(); + EXPECT_CALL(fake_agent_, FirstUserGestureObservedInTab()); + base::RunLoop().RunUntilIdle(); +} + +TEST_F(ContentAutofillDriverTest, FirstUserGestureObserved) { + EXPECT_CALL(*test_autofill_client_, OnFirstUserGestureObserved()); + driver_->FirstUserGestureObserved(); +} + } // namespace autofill diff --git a/chromium/components/autofill/content/browser/risk/fingerprint.cc b/chromium/components/autofill/content/browser/risk/fingerprint.cc index acac2f15ad8..09041403ccc 100644 --- a/chromium/components/autofill/content/browser/risk/fingerprint.cc +++ b/chromium/components/autofill/content/browser/risk/fingerprint.cc @@ -41,6 +41,7 @@ #include "device/geolocation/geolocation_provider.h" #include "device/geolocation/geoposition.h" #include "gpu/config/gpu_info.h" +#include "ppapi/features/features.h" #include "third_party/WebKit/public/platform/WebRect.h" #include "ui/display/display.h" #include "ui/display/screen.h" @@ -293,7 +294,7 @@ FingerprintDataLoader::FingerprintDataLoader( gpu_data_manager_->RequestCompleteGpuInfoIfNeeded(); } -#if defined(ENABLE_PLUGINS) +#if BUILDFLAG(ENABLE_PLUGINS) // Load plugin data. content::PluginService::GetInstance()->GetPlugins( base::Bind(&FingerprintDataLoader::OnGotPlugins, @@ -325,7 +326,7 @@ void FingerprintDataLoader::OnGpuInfoUpdate() { void FingerprintDataLoader::OnGotFonts(std::unique_ptr<base::ListValue> fonts) { DCHECK(!fonts_); - fonts_.reset(fonts.release()); + fonts_ = std::move(fonts); MaybeFillFingerprint(); } diff --git a/chromium/components/autofill/content/browser/risk/fingerprint.h b/chromium/components/autofill/content/browser/risk/fingerprint.h index ae1b7857a7a..870743d1a55 100644 --- a/chromium/components/autofill/content/browser/risk/fingerprint.h +++ b/chromium/components/autofill/content/browser/risk/fingerprint.h @@ -20,8 +20,6 @@ #include "base/callback_forward.h" #include "components/autofill/core/browser/autofill_client.h" -class PrefService; - namespace base { class Time; } diff --git a/chromium/components/autofill/content/common/autofill_agent.mojom b/chromium/components/autofill/content/common/autofill_agent.mojom index a2f40a9da67..7870cafc8b4 100644 --- a/chromium/components/autofill/content/common/autofill_agent.mojom +++ b/chromium/components/autofill/content/common/autofill_agent.mojom @@ -5,7 +5,7 @@ module autofill.mojom; import "components/autofill/content/common/autofill_types.mojom"; -import "mojo/common/common_custom_types.mojom"; +import "mojo/common/string16.mojom"; // There is one instance of this interface per render frame in the render // process. diff --git a/chromium/components/autofill/content/common/autofill_driver.mojom b/chromium/components/autofill/content/common/autofill_driver.mojom index 51278195f4b..ce08780300c 100644 --- a/chromium/components/autofill/content/common/autofill_driver.mojom +++ b/chromium/components/autofill/content/common/autofill_driver.mojom @@ -5,7 +5,9 @@ module autofill.mojom; import "components/autofill/content/common/autofill_types.mojom"; -import "mojo/common/common_custom_types.mojom"; +import "mojo/common/string16.mojom"; +import "mojo/common/text_direction.mojom"; +import "mojo/common/time.mojom"; import "ui/gfx/geometry/mojo/geometry.mojom"; // There is one instance of this interface per render frame host in the browser @@ -39,9 +41,6 @@ interface AutofillDriver { // Instructs the browser to hide the Autofill popup if it is open. HidePopup(); - // Sent immediately after the renderer receives a ping IPC. - PingAck(); - // Sent when the current form is no longer focused. FocusNoLongerOnForm(); @@ -62,11 +61,6 @@ interface AutofillDriver { // There is one instance of this interface per render frame host in the browser // process. interface PasswordManagerDriver { - // A ping to the browser that PasswordAutofillAgent was constructed. As a - // consequence, the browser sends SetLoggingState with the current - // state of the logging activity. - PasswordAutofillAgentConstructed(); - // Notification that password forms have been seen that are candidates for // filling/submitting by the password manager. PasswordFormsParsed(array<PasswordForm> forms); @@ -93,10 +87,17 @@ interface PasswordManagerDriver { // Instructs the browser to show a popup with suggestions filled from data // associated with |key|. The popup will use |text_direction| for displaying // text. - ShowPasswordSuggestions(int32 key, TextDirection text_direction, + ShowPasswordSuggestions(int32 key, + mojo.common.mojom.TextDirection text_direction, mojo.common.mojom.String16 typed_username, int32 options, gfx.mojom.RectF bounds); + // Instructs the browser to show a popup with a warning that the form + // is not secure. The popup will use |text_direction| for displaying + // text. This popup is shown when a password form on a non-secure page is + // autofilled on page load. + ShowNotSecureWarning(mojo.common.mojom.TextDirection text_direction, + gfx.mojom.RectF bounds); // Instructs the browser to presave the form with generated password. PresaveGeneratedPassword(PasswordForm password_form); diff --git a/chromium/components/autofill/content/common/autofill_param_traits_macros.h b/chromium/components/autofill/content/common/autofill_param_traits_macros.h index 1bced6ec0ab..edd3fcb8c05 100644 --- a/chromium/components/autofill/content/common/autofill_param_traits_macros.h +++ b/chromium/components/autofill/content/common/autofill_param_traits_macros.h @@ -21,7 +21,7 @@ IPC_ENUM_TRAITS_MAX_VALUE(autofill::FormFieldData::RoleAttribute, autofill::FormFieldData::ROLE_ATTRIBUTE_OTHER) IPC_ENUM_TRAITS_MAX_VALUE(base::i18n::TextDirection, - base::i18n::TEXT_DIRECTION_NUM_DIRECTIONS - 1) + base::i18n::TEXT_DIRECTION_MAX) IPC_STRUCT_TRAITS_BEGIN(autofill::FormFieldData) IPC_STRUCT_TRAITS_MEMBER(label) diff --git a/chromium/components/autofill/content/common/autofill_types.mojom b/chromium/components/autofill/content/common/autofill_types.mojom index 91905f2d84d..ec56870ac77 100644 --- a/chromium/components/autofill/content/common/autofill_types.mojom +++ b/chromium/components/autofill/content/common/autofill_types.mojom @@ -4,7 +4,8 @@ module autofill.mojom; -import "mojo/common/common_custom_types.mojom"; +import "mojo/common/text_direction.mojom"; +import "mojo/common/time.mojom"; import "url/mojo/origin.mojom"; import "url/mojo/url.mojom"; @@ -21,14 +22,6 @@ enum RoleAttribute { ROLE_ATTRIBUTE_OTHER }; -// base::i18n::TextDirection -enum TextDirection { - UNKNOWN_DIRECTION = 0, - RIGHT_TO_LEFT = 1, - LEFT_TO_RIGHT = 2, - TEXT_DIRECTION_NUM_DIRECTIONS = 3 -}; - // autofill::PasswordForm::GenerationUploadStatus enum GenerationUploadStatus { NO_SIGNAL_SENT, @@ -84,7 +77,7 @@ struct FormFieldData { bool is_focusable; bool should_autocomplete; RoleAttribute role; - TextDirection text_direction; + mojo.common.mojom.TextDirection text_direction; array<string> option_values; array<string> option_contents; diff --git a/chromium/components/autofill/content/common/autofill_types.typemap b/chromium/components/autofill/content/common/autofill_types.typemap index 4511910b355..7072d03c967 100644 --- a/chromium/components/autofill/content/common/autofill_types.typemap +++ b/chromium/components/autofill/content/common/autofill_types.typemap @@ -42,6 +42,5 @@ type_mappings = [ "autofill.mojom.PasswordFormScheme=autofill::PasswordForm::Scheme", "autofill.mojom.PasswordFormType=autofill::PasswordForm::Type", "autofill.mojom.RoleAttribute=autofill::FormFieldData::RoleAttribute", - "autofill.mojom.TextDirection=base::i18n::TextDirection", "autofill.mojom.UsernamesCollectionKey=autofill::UsernamesCollectionKey", ] diff --git a/chromium/components/autofill/content/common/autofill_types_struct_traits.cc b/chromium/components/autofill/content/common/autofill_types_struct_traits.cc index 81e2fb478c6..c536b4abd48 100644 --- a/chromium/components/autofill/content/common/autofill_types_struct_traits.cc +++ b/chromium/components/autofill/content/common/autofill_types_struct_traits.cc @@ -6,6 +6,7 @@ #include "base/i18n/rtl.h" #include "ipc/ipc_message_utils.h" +#include "mojo/common/common_custom_types_struct_traits.h" #include "url/mojo/origin_struct_traits.h" #include "url/mojo/url_gurl_struct_traits.h" @@ -83,48 +84,6 @@ bool EnumTraits<mojom::RoleAttribute, FormFieldData::RoleAttribute>::FromMojom( } // static -mojom::TextDirection -EnumTraits<mojom::TextDirection, base::i18n::TextDirection>::ToMojom( - base::i18n::TextDirection input) { - switch (input) { - case base::i18n::TextDirection::UNKNOWN_DIRECTION: - return mojom::TextDirection::UNKNOWN_DIRECTION; - case base::i18n::TextDirection::RIGHT_TO_LEFT: - return mojom::TextDirection::RIGHT_TO_LEFT; - case base::i18n::TextDirection::LEFT_TO_RIGHT: - return mojom::TextDirection::LEFT_TO_RIGHT; - case base::i18n::TextDirection::TEXT_DIRECTION_NUM_DIRECTIONS: - return mojom::TextDirection::TEXT_DIRECTION_NUM_DIRECTIONS; - } - - NOTREACHED(); - return mojom::TextDirection::UNKNOWN_DIRECTION; -} - -// static -bool EnumTraits<mojom::TextDirection, base::i18n::TextDirection>::FromMojom( - mojom::TextDirection input, - base::i18n::TextDirection* output) { - switch (input) { - case mojom::TextDirection::UNKNOWN_DIRECTION: - *output = base::i18n::TextDirection::UNKNOWN_DIRECTION; - return true; - case mojom::TextDirection::RIGHT_TO_LEFT: - *output = base::i18n::TextDirection::RIGHT_TO_LEFT; - return true; - case mojom::TextDirection::LEFT_TO_RIGHT: - *output = base::i18n::TextDirection::LEFT_TO_RIGHT; - return true; - case mojom::TextDirection::TEXT_DIRECTION_NUM_DIRECTIONS: - *output = base::i18n::TextDirection::TEXT_DIRECTION_NUM_DIRECTIONS; - return true; - } - - NOTREACHED(); - return false; -} - -// static mojom::GenerationUploadStatus EnumTraits<mojom::GenerationUploadStatus, PasswordForm::GenerationUploadStatus>:: ToMojom(PasswordForm::GenerationUploadStatus input) { diff --git a/chromium/components/autofill/content/common/autofill_types_struct_traits.h b/chromium/components/autofill/content/common/autofill_types_struct_traits.h index 8e2f74031ab..c623aa50b30 100644 --- a/chromium/components/autofill/content/common/autofill_types_struct_traits.h +++ b/chromium/components/autofill/content/common/autofill_types_struct_traits.h @@ -7,6 +7,7 @@ #include <utility> +#include "base/i18n/rtl.h" #include "base/strings/string16.h" #include "components/autofill/content/common/autofill_types.mojom.h" #include "components/autofill/core/common/form_data.h" @@ -40,14 +41,6 @@ struct EnumTraits<autofill::mojom::RoleAttribute, }; template <> -struct EnumTraits<autofill::mojom::TextDirection, base::i18n::TextDirection> { - static autofill::mojom::TextDirection ToMojom( - base::i18n::TextDirection input); - static bool FromMojom(autofill::mojom::TextDirection input, - base::i18n::TextDirection* output); -}; - -template <> struct EnumTraits<autofill::mojom::GenerationUploadStatus, autofill::PasswordForm::GenerationUploadStatus> { static autofill::mojom::GenerationUploadStatus ToMojom( diff --git a/chromium/components/autofill/content/renderer/BUILD.gn b/chromium/components/autofill/content/renderer/BUILD.gn index 5dd8086ea43..2ddd7715dc2 100644 --- a/chromium/components/autofill/content/renderer/BUILD.gn +++ b/chromium/components/autofill/content/renderer/BUILD.gn @@ -30,6 +30,10 @@ static_library("renderer") { "//base:i18n", "//components/autofill/content/common:mojo_interfaces", "//components/autofill/core/common", + + # TODO(elawrence): remove security_state/core when the Form-Not-Secure + # feature is fully launched. https://crbug.com/677295 + "//components/security_state/core", "//components/strings", "//content/public/common", "//content/public/renderer", diff --git a/chromium/components/autofill/content/renderer/DEPS b/chromium/components/autofill/content/renderer/DEPS index 0885896b9c9..83ce085fa2a 100644 --- a/chromium/components/autofill/content/renderer/DEPS +++ b/chromium/components/autofill/content/renderer/DEPS @@ -3,3 +3,11 @@ include_rules = [ "+content/public/renderer", "+third_party/re2", ] + +specific_include_rules = { + # TODO(elawrence): remove this when the Form-Not-Secure feature is fully + # launched. https://crbug.com/677295 + "password_autofill_agent\.cc" : [ + "+components/security_state/core", + ], +} diff --git a/chromium/components/autofill/content/renderer/autofill_agent.cc b/chromium/components/autofill/content/renderer/autofill_agent.cc index 49cf6727451..0aa8a423a75 100644 --- a/chromium/components/autofill/content/renderer/autofill_agent.cc +++ b/chromium/components/autofill/content/renderer/autofill_agent.cc @@ -300,10 +300,6 @@ void AutofillAgent::FocusChangeComplete() { } } -void AutofillAgent::setIgnoreTextChanges(bool ignore) { - ignore_text_changes_ = ignore; -} - void AutofillAgent::FormControlElementClicked( const WebFormControlElement& element, bool was_focused) { @@ -487,10 +483,6 @@ void AutofillAgent::PreviewForm(int32_t id, const FormData& form) { GetAutofillDriver()->DidPreviewAutofillFormData(); } -void AutofillAgent::OnPing() { - GetAutofillDriver()->PingAck(); -} - void AutofillAgent::FieldTypePredictionsAvailable( const std::vector<FormDataPredictions>& forms) { for (const auto& form : forms) { @@ -577,6 +569,14 @@ void AutofillAgent::ShowInitialPasswordAccountSuggestions( ShowSuggestions(element, options); } +void AutofillAgent::ShowNotSecureWarning( + const blink::WebInputElement& element) { + if (is_generation_popup_possibly_visible_) + return; + password_autofill_agent_->ShowNotSecureWarning(element); + is_popup_possibly_visible_ = true; +} + void AutofillAgent::OnSamePageNavigationCompleted() { if (last_interacted_form_.isNull()) { // If no last interacted form is available (i.e., there is no form tag), @@ -783,7 +783,7 @@ void AutofillAgent::ajaxSucceeded() { const mojom::AutofillDriverPtr& AutofillAgent::GetAutofillDriver() { if (!autofill_driver_) { render_frame()->GetRemoteInterfaces()->GetInterface( - mojo::GetProxy(&autofill_driver_)); + mojo::MakeRequest(&autofill_driver_)); } return autofill_driver_; diff --git a/chromium/components/autofill/content/renderer/autofill_agent.h b/chromium/components/autofill/content/renderer/autofill_agent.h index e464af9575b..698e5c9fc70 100644 --- a/chromium/components/autofill/content/renderer/autofill_agent.h +++ b/chromium/components/autofill/content/renderer/autofill_agent.h @@ -83,6 +83,8 @@ class AutofillAgent : public content::RenderFrameObserver, int32_t key, const PasswordFormFillData& form_data) override; + void ShowNotSecureWarning(const blink::WebInputElement& element); + protected: // blink::WebAutofillClient: void didAssociateFormControlsDynamically() override; @@ -182,14 +184,11 @@ class AutofillAgent : public content::RenderFrameObserver, void textFieldDidReceiveKeyDown( const blink::WebInputElement& element, const blink::WebKeyboardEvent& event) override; - void setIgnoreTextChanges(bool ignore) override; void openTextDataListChooser(const blink::WebInputElement& element) override; void dataListOptionsChanged(const blink::WebInputElement& element) override; void firstUserGestureObserved() override; void ajaxSucceeded() override; - void OnPing(); - // Called when a same-page navigation is detected. void OnSamePageNavigationCompleted(); // Helper method which collects unowned elements (i.e., those not inside a diff --git a/chromium/components/autofill/content/renderer/form_autofill_util.cc b/chromium/components/autofill/content/renderer/form_autofill_util.cc index 2c5dc0975a7..8d690c2446a 100644 --- a/chromium/components/autofill/content/renderer/form_autofill_util.cc +++ b/chromium/components/autofill/content/renderer/form_autofill_util.cc @@ -5,11 +5,13 @@ #include "components/autofill/content/renderer/form_autofill_util.h" #include <map> +#include <memory> #include <set> +#include <vector> #include "base/command_line.h" #include "base/logging.h" -#include "base/memory/scoped_vector.h" +#include "base/memory/ptr_util.h" #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" @@ -963,7 +965,7 @@ bool ExtractFieldsFromControlElements( const WebVector<WebFormControlElement>& control_elements, const FieldValueAndPropertiesMaskMap* field_value_and_properties_map, ExtractMask extract_mask, - ScopedVector<FormFieldData>* form_fields, + std::vector<std::unique_ptr<FormFieldData>>* form_fields, std::vector<bool>* fields_extracted, std::map<WebFormControlElement, FormFieldData*>* element_map) { DCHECK(form_fields->empty()); @@ -981,7 +983,7 @@ bool ExtractFieldsFromControlElements( WebFormControlElementToFormField(control_element, field_value_and_properties_map, extract_mask, form_field); - form_fields->push_back(form_field); + form_fields->push_back(base::WrapUnique(form_field)); (*element_map)[control_element] = form_field; (*fields_extracted)[i] = true; @@ -1083,7 +1085,7 @@ bool FormOrFieldsetsToFormData( // The extracted FormFields. We use pointers so we can store them in // |element_map|. - ScopedVector<FormFieldData> form_fields; + std::vector<std::unique_ptr<FormFieldData>> form_fields; // A vector of bools that indicate whether each field in the form meets the // requirements and thus will be in the resulting |form|. @@ -1152,8 +1154,8 @@ bool FormOrFieldsetsToFormData( } // Copy the created FormFields into the resulting FormData object. - for (const auto* iter : form_fields) - form->fields.push_back(*iter); + for (const auto& field : form_fields) + form->fields.push_back(*field); return true; } @@ -1617,6 +1619,8 @@ bool UnownedPasswordFormElementsAndFieldSetsToFormData( bool FindFormAndFieldForFormControlElement(const WebFormControlElement& element, FormData* form, FormFieldData* field) { + DCHECK(!element.isNull()); + if (!IsAutofillableElement(element)) return false; diff --git a/chromium/components/autofill/content/renderer/form_cache.h b/chromium/components/autofill/content/renderer/form_cache.h index 78bef2e6447..4afb3a66f9e 100644 --- a/chromium/components/autofill/content/renderer/form_cache.h +++ b/chromium/components/autofill/content/renderer/form_cache.h @@ -16,9 +16,6 @@ #include "components/autofill/core/common/form_data.h" namespace blink { -class WebDocument; -class WebElement; -class WebElementCollection; class WebFormControlElement; class WebFrame; class WebInputElement; diff --git a/chromium/components/autofill/content/renderer/page_click_tracker.cc b/chromium/components/autofill/content/renderer/page_click_tracker.cc index da70ec2e851..0af746045e8 100644 --- a/chromium/components/autofill/content/renderer/page_click_tracker.cc +++ b/chromium/components/autofill/content/renderer/page_click_tracker.cc @@ -10,7 +10,6 @@ #include "components/autofill/core/common/autofill_util.h" #include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_view.h" -#include "third_party/WebKit/public/platform/WebInputEvent.h" #include "third_party/WebKit/public/platform/WebPoint.h" #include "third_party/WebKit/public/platform/WebSize.h" #include "third_party/WebKit/public/web/WebDocument.h" @@ -25,8 +24,6 @@ using blink::WebElement; using blink::WebFormControlElement; using blink::WebGestureEvent; using blink::WebInputElement; -using blink::WebInputEvent; -using blink::WebMouseEvent; using blink::WebNode; using blink::WebPoint; using blink::WebSize; diff --git a/chromium/components/autofill/content/renderer/password_autofill_agent.cc b/chromium/components/autofill/content/renderer/password_autofill_agent.cc index 25edf2c8888..b17f17f0de1 100644 --- a/chromium/components/autofill/content/renderer/password_autofill_agent.cc +++ b/chromium/components/autofill/content/renderer/password_autofill_agent.cc @@ -9,16 +9,17 @@ #include <memory> #include <string> #include <utility> +#include <vector> #include "base/bind.h" #include "base/i18n/case_conversion.h" #include "base/memory/linked_ptr.h" #include "base/memory/ptr_util.h" -#include "base/memory/scoped_vector.h" #include "base/message_loop/message_loop.h" #include "base/metrics/histogram_macros.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" +#include "base/threading/thread_task_runner_handle.h" #include "build/build_config.h" #include "components/autofill/content/renderer/form_autofill_util.h" #include "components/autofill/content/renderer/password_form_conversion_utils.h" @@ -27,6 +28,8 @@ #include "components/autofill/core/common/autofill_util.h" #include "components/autofill/core/common/form_field_data.h" #include "components/autofill/core/common/password_form_fill_data.h" +#include "components/security_state/core/security_state.h" +#include "content/public/common/origin_util.h" #include "content/public/renderer/document_state.h" #include "content/public/renderer/navigation_state.h" #include "content/public/renderer/render_frame.h" @@ -177,7 +180,8 @@ bool FindFormInputElement( // fields. const blink::WebInputElement input_element = control_element.toConst<blink::WebInputElement>(); - if (input_element.isPasswordField() != is_password_field) + if (!input_element.isTextField() || + input_element.isPasswordField() != is_password_field) continue; // For change password form with ambiguous or empty names keep only the @@ -243,8 +247,6 @@ void FindFormElements(content::RenderFrame* render_frame, DCHECK(results); blink::WebDocument doc = render_frame->GetWebFrame()->document(); - if (!doc.isHTMLDocument()) - return; if (data.origin != form_util::GetCanonicalOriginForDocument(doc)) return; @@ -559,19 +561,6 @@ bool FillFormOnPasswordReceived( field_value_and_properties_map, registration_callback, logger); } -// Takes a |map| with pointers as keys and linked_ptr as values, and returns -// true if |key| is not NULL and |map| contains a non-NULL entry for |key|. -// Makes sure not to create an entry as a side effect of using the operator []. -template <class Key, class Value> -bool ContainsNonNullEntryForNonNullKey( - const std::map<Key*, linked_ptr<Value>>& map, - Key* key) { - if (!key) - return false; - auto it = map.find(key); - return it != map.end() && it->second.get(); -} - } // namespace //////////////////////////////////////////////////////////////////////////////// @@ -586,7 +575,6 @@ PasswordAutofillAgent::PasswordAutofillAgent(content::RenderFrame* render_frame) // PasswordAutofillAgent is guaranteed to outlive |render_frame|. render_frame->GetInterfaceRegistry()->AddInterface( base::Bind(&PasswordAutofillAgent::BindRequest, base::Unretained(this))); - GetPasswordManagerDriver()->PasswordAutofillAgentConstructed(); } PasswordAutofillAgent::~PasswordAutofillAgent() { @@ -855,9 +843,23 @@ bool PasswordAutofillAgent::ShowSuggestions( blink::WebInputElement username_element; blink::WebInputElement password_element; PasswordInfo* password_info; + if (!FindPasswordInfoForElement(element, &username_element, &password_element, - &password_info)) + &password_info)) { + // If we don't have a password stored, but the form is non-secure, warn + // the user about the non-secure form. + if ((element.isPasswordField() || + HasAutocompleteAttributeValue(element, "username")) && + security_state::IsHttpWarningInFormEnabled() && + !content::IsOriginSecure( + url::Origin( + render_frame()->GetWebFrame()->top()->getSecurityOrigin()) + .GetURL())) { + autofill_agent_->ShowNotSecureWarning(element); + return true; + } return false; + } // If autocomplete='off' is set on the form elements, no suggestion dialog // should be shown. However, return |true| to indicate that this is a known @@ -899,6 +901,16 @@ bool PasswordAutofillAgent::ShowSuggestions( element.isPasswordField()); } +void PasswordAutofillAgent::ShowNotSecureWarning( + const blink::WebInputElement& element) { + FormData form; + FormFieldData field; + form_util::FindFormAndFieldForFormControlElement(element, &form, &field); + GetPasswordManagerDriver()->ShowNotSecureWarning( + field.text_direction, + render_frame()->GetRenderView()->ElementBoundsInWindow(element)); +} + bool PasswordAutofillAgent::OriginCanAccessPasswordManager( const blink::WebSecurityOrigin& origin) { return origin.canAccessPasswordManager(); @@ -947,10 +959,6 @@ void PasswordAutofillAgent::SendPasswordForms(bool only_visible) { } blink::WebLocalFrame* frame = render_frame()->GetWebFrame(); - // RenderFrameObserver::DidFinishLoad() can fire when Frame is - // detaching. crbug.com/654654 - if (frame->isFrameDetachedForSpecialOneOffStopTheCrashingHackBug561873()) - return; // Make sure that this security origin is allowed to use password manager. blink::WebSecurityOrigin origin = frame->document().getSecurityOrigin(); @@ -1195,7 +1203,7 @@ void PasswordAutofillAgent::DidStartProvisionalLoad() { *provisionally_saved_form_); provisionally_saved_form_.reset(); } else { - ScopedVector<PasswordForm> possible_submitted_forms; + std::vector<std::unique_ptr<PasswordForm>> possible_submitted_forms; // Loop through the forms on the page looking for one that has been // filled out. If one exists, try and save the credentials. blink::WebVector<blink::WebFormElement> forms; @@ -1218,7 +1226,7 @@ void PasswordAutofillAgent::DidStartProvisionalLoad() { *render_frame()->GetWebFrame(), &field_value_and_properties_map_, &form_predictions_)); - for (const PasswordForm* password_form : possible_submitted_forms) { + for (const auto& password_form : possible_submitted_forms) { if (password_form && !password_form->username_value.empty() && FormContainsNonDefaultPasswordValue(*password_form)) { password_forms_found = true; @@ -1506,7 +1514,7 @@ const mojom::PasswordManagerDriverPtr& PasswordAutofillAgent::GetPasswordManagerDriver() { if (!password_manager_driver_) { render_frame()->GetRemoteInterfaces()->GetInterface( - mojo::GetProxy(&password_manager_driver_)); + mojo::MakeRequest(&password_manager_driver_)); } return password_manager_driver_; diff --git a/chromium/components/autofill/content/renderer/password_autofill_agent.h b/chromium/components/autofill/content/renderer/password_autofill_agent.h index 48f61a493f5..ce63053cce8 100644 --- a/chromium/components/autofill/content/renderer/password_autofill_agent.h +++ b/chromium/components/autofill/content/renderer/password_autofill_agent.h @@ -25,7 +25,6 @@ namespace blink { class WebInputElement; -class WebKeyboardEvent; class WebSecurityOrigin; } @@ -95,6 +94,11 @@ 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(); 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 d41ac97fbe3..3465657dd93 100644 --- a/chromium/components/autofill/content/renderer/password_form_conversion_utils.cc +++ b/chromium/components/autofill/content/renderer/password_form_conversion_utils.cc @@ -154,12 +154,16 @@ void ExcludeUsernameFromOtherUsernamesList( } // 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. +// 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. bool LocateSpecificPasswords(std::vector<WebInputElement> passwords, WebInputElement* current_password, - WebInputElement* new_password) { + WebInputElement* new_password, + WebInputElement* confirmation_password) { DCHECK(current_password && current_password->isNull()); DCHECK(new_password && new_password->isNull()); + DCHECK(confirmation_password && confirmation_password->isNull()); // 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 @@ -171,6 +175,9 @@ bool LocateSpecificPasswords(std::vector<WebInputElement> passwords, } else if (HasAutocompleteAttributeValue(it, kAutocompleteNewPassword) && new_password->isNull()) { *new_password = it; + } else if (!new_password->isNull() && + (new_password->value() == it.value())) { + *confirmation_password = it; } } @@ -197,6 +204,7 @@ bool LocateSpecificPasswords(std::vector<WebInputElement> passwords, // 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 @@ -218,12 +226,14 @@ bool LocateSpecificPasswords(std::vector<WebInputElement> passwords, // 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, so no luck. @@ -505,7 +515,9 @@ bool GetPasswordForm( WebInputElement password; WebInputElement new_password; - if (!LocateSpecificPasswords(passwords, &password, &new_password)) + WebInputElement confirmation_password; + if (!LocateSpecificPasswords(passwords, &password, &new_password, + &confirmation_password)) return false; DCHECK_EQ(passwords.size(), last_text_input_before_password.size()); @@ -584,6 +596,10 @@ bool GetPasswordForm( new_password.getAttribute("value") == new_password.value(); if (HasAutocompleteAttributeValue(new_password, kAutocompleteNewPassword)) password_form->new_password_marked_by_site = true; + if (!confirmation_password.isNull()) { + password_form->confirmation_password_element = + FieldName(confirmation_password, "anonymous_confirmation_password"); + } } if (username_element.isNull()) { 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 4101dcb9460..4c8cd645e46 100644 --- a/chromium/components/autofill/content/renderer/password_form_conversion_utils.h +++ b/chromium/components/autofill/content/renderer/password_form_conversion_utils.h @@ -15,7 +15,6 @@ #include "url/gurl.h" namespace blink { -class WebDocument; class WebFormElement; class WebFormControlElement; class WebFrame; @@ -24,8 +23,6 @@ class WebInputElement; namespace autofill { -struct FormData; -struct FormFieldData; struct PasswordForm; // Tests whether the given form is a GAIA reauthentication form. The form is 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 5169e0bc376..08b1988e158 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 @@ -213,9 +213,8 @@ class MAYBE_PasswordFormConversionUtilsTest : public content::RenderViewTest { FormStructure form_structure(form_data); int field_index = 0; - for (std::vector<AutofillField *>::const_iterator - field = form_structure.begin(); - field != form_structure.end(); ++field, ++field_index) { + 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)] = @@ -384,19 +383,20 @@ TEST_F(MAYBE_PasswordFormConversionUtilsTest, IdentifyingTwoPasswordFields) { 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"}, + {{"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", ""}, + {{"", ""}, "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"}}; + {{"alpha", ""}, "password1", "alpha", "password2", "", ""}, + {{"", "beta"}, "password1", "", "password2", "beta", ""}, + {{"alpha", "beta"}, "password1", "alpha", "password2", "beta", ""}}; for (size_t i = 0; i < arraysize(cases); ++i) { SCOPED_TRACE(testing::Message() << "Iteration " << i); @@ -421,6 +421,8 @@ TEST_F(MAYBE_PasswordFormConversionUtilsTest, IdentifyingTwoPasswordFields) { 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("username1"), password_form->username_element); @@ -439,24 +441,30 @@ TEST_F(MAYBE_PasswordFormConversionUtilsTest, IdentifyingThreePasswordFields) { 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", ""}, - {{"", "beta", "beta"}, "password1", "", "password2", "beta"}, - {{"alpha", "beta", "beta"}, "password1", "alpha", "password2", "beta"}, + {{"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"}, + {{"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", ""}}; - // Note: In all other cases, we give up and consider the form invalid. - // This is tested in InvalidFormDueToConfusingPasswordFields. + {{"", "", ""}, "password1", "", "password2", "", "password3"}}; + // Note: In all other cases, we give up and consider the form invalid. + // This is tested in InvalidFormDueToConfusingPasswordFields. for (size_t i = 0; i < arraysize(cases); ++i) { SCOPED_TRACE(testing::Message() << "Iteration " << i); @@ -482,6 +490,8 @@ TEST_F(MAYBE_PasswordFormConversionUtilsTest, IdentifyingThreePasswordFields) { 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("username1"), password_form->username_element); diff --git a/chromium/components/autofill/content/renderer/password_generation_agent.cc b/chromium/components/autofill/content/renderer/password_generation_agent.cc index a0a52834799..183b5f068c2 100644 --- a/chromium/components/autofill/content/renderer/password_generation_agent.cc +++ b/chromium/components/autofill/content/renderer/password_generation_agent.cc @@ -8,6 +8,7 @@ #include "base/command_line.h" #include "base/logging.h" +#include "base/threading/thread_task_runner_handle.h" #include "components/autofill/content/renderer/form_autofill_util.h" #include "components/autofill/content/renderer/form_classifier.h" #include "components/autofill/content/renderer/password_autofill_agent.h" diff --git a/chromium/components/autofill/content/renderer/password_generation_agent.h b/chromium/components/autofill/content/renderer/password_generation_agent.h index 2b73a4dc01d..694c5fc990c 100644 --- a/chromium/components/autofill/content/renderer/password_generation_agent.h +++ b/chromium/components/autofill/content/renderer/password_generation_agent.h @@ -21,13 +21,8 @@ #include "third_party/WebKit/public/web/WebInputElement.h" #include "url/gurl.h" -namespace blink { -class WebDocument; -} - namespace autofill { -struct FormData; struct PasswordForm; struct PasswordFormGenerationData; class PasswordAutofillAgent; 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 7c0c27e21c9..85958d4f696 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 @@ -61,7 +61,8 @@ class FakeContentPasswordManagerDriver : public mojom::PasswordManagerDriver { int options, const gfx::RectF& bounds) override {} - void PasswordAutofillAgentConstructed() override {} + void ShowNotSecureWarning(base::i18n::TextDirection text_direction, + const gfx::RectF& bounds) override {} void RecordSavePasswordProgress(const std::string& log) override { called_record_save_ = true; diff --git a/chromium/components/autofill/core/browser/BUILD.gn b/chromium/components/autofill/core/browser/BUILD.gn index 4e919e7d359..5281b7de472 100644 --- a/chromium/components/autofill/core/browser/BUILD.gn +++ b/chromium/components/autofill/core/browser/BUILD.gn @@ -117,6 +117,8 @@ static_library("browser") { "ui/card_unmask_prompt_view.h", "validation.cc", "validation.h", + "webdata/autocomplete_sync_bridge.cc", + "webdata/autocomplete_sync_bridge.h", "webdata/autocomplete_syncable_service.cc", "webdata/autocomplete_syncable_service.h", "webdata/autofill_change.cc", @@ -125,6 +127,8 @@ static_library("browser") { "webdata/autofill_data_type_controller.h", "webdata/autofill_entry.cc", "webdata/autofill_entry.h", + "webdata/autofill_metadata_change_list.cc", + "webdata/autofill_metadata_change_list.h", "webdata/autofill_profile_data_type_controller.cc", "webdata/autofill_profile_data_type_controller.h", "webdata/autofill_profile_syncable_service.cc", @@ -316,10 +320,12 @@ source_set("unit_tests") { "phone_number_unittest.cc", "ui/card_unmask_prompt_controller_impl_unittest.cc", "validation_unittest.cc", + "webdata/autocomplete_sync_bridge_unittest.cc", "webdata/autofill_data_type_controller_unittest.cc", "webdata/autofill_profile_syncable_service_unittest.cc", "webdata/autofill_table_unittest.cc", "webdata/autofill_wallet_metadata_syncable_service_unittest.cc", + "webdata/autofill_wallet_syncable_service_unittest.cc", "webdata/web_data_service_unittest.cc", ] diff --git a/chromium/components/autofill/core/browser/address_field_unittest.cc b/chromium/components/autofill/core/browser/address_field_unittest.cc index b3f4f92ca18..d959e8d7740 100644 --- a/chromium/components/autofill/core/browser/address_field_unittest.cc +++ b/chromium/components/autofill/core/browser/address_field_unittest.cc @@ -5,10 +5,10 @@ #include "components/autofill/core/browser/address_field.h" #include <memory> +#include <vector> #include "base/macros.h" #include "base/memory/ptr_util.h" -#include "base/memory/scoped_vector.h" #include "base/strings/string16.h" #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/autofill_field.h" @@ -25,7 +25,7 @@ class AddressFieldTest : public testing::Test { AddressFieldTest() {} protected: - ScopedVector<AutofillField> list_; + std::vector<std::unique_ptr<AutofillField>> list_; std::unique_ptr<AddressField> field_; FieldCandidatesMap field_candidates_map_; @@ -40,14 +40,14 @@ class AddressFieldTest : public testing::Test { }; TEST_F(AddressFieldTest, Empty) { - AutofillScanner scanner(list_.get()); + AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_EQ(nullptr, field_.get()); } TEST_F(AddressFieldTest, NonParse) { - list_.push_back(new AutofillField); - AutofillScanner scanner(list_.get()); + list_.push_back(base::MakeUnique<AutofillField>()); + AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_EQ(nullptr, field_.get()); } @@ -58,9 +58,10 @@ TEST_F(AddressFieldTest, ParseOneLineAddress) { field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("addr1"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("addr1"))); - AutofillScanner scanner(list_.get()); + AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); field_->AddClassifications(&field_candidates_map_); @@ -76,13 +77,15 @@ TEST_F(AddressFieldTest, ParseTwoLineAddress) { field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("addr1"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("addr1"))); field.label = base::string16(); field.name = ASCIIToUTF16("address2"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("addr2"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("addr2"))); - AutofillScanner scanner(list_.get()); + AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); field_->AddClassifications(&field_candidates_map_); @@ -102,17 +105,20 @@ TEST_F(AddressFieldTest, ParseThreeLineAddress) { field.label = ASCIIToUTF16("Address Line1"); field.name = ASCIIToUTF16("Address1"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("addr1"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("addr1"))); field.label = ASCIIToUTF16("Address Line2"); field.name = ASCIIToUTF16("Address2"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("addr2"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("addr2"))); field.label = ASCIIToUTF16("Address Line3"); field.name = ASCIIToUTF16("Address3"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("addr3"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("addr3"))); - AutofillScanner scanner(list_.get()); + AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); field_->AddClassifications(&field_candidates_map_); @@ -136,9 +142,9 @@ TEST_F(AddressFieldTest, ParseStreetAddressFromTextArea) { field.label = ASCIIToUTF16("Address"); field.name = ASCIIToUTF16("address"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("addr"))); + list_.push_back(base::MakeUnique<AutofillField>(field, ASCIIToUTF16("addr"))); - AutofillScanner scanner(list_.get()); + AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); field_->AddClassifications(&field_candidates_map_); @@ -154,9 +160,10 @@ TEST_F(AddressFieldTest, ParseCity) { field.label = ASCIIToUTF16("City"); field.name = ASCIIToUTF16("city"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("city1"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("city1"))); - AutofillScanner scanner(list_.get()); + AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); field_->AddClassifications(&field_candidates_map_); @@ -172,9 +179,10 @@ TEST_F(AddressFieldTest, ParseState) { field.label = ASCIIToUTF16("State"); field.name = ASCIIToUTF16("state"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("state1"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("state1"))); - AutofillScanner scanner(list_.get()); + AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); field_->AddClassifications(&field_candidates_map_); @@ -190,9 +198,9 @@ TEST_F(AddressFieldTest, ParseZip) { field.label = ASCIIToUTF16("Zip"); field.name = ASCIIToUTF16("zip"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("zip1"))); + list_.push_back(base::MakeUnique<AutofillField>(field, ASCIIToUTF16("zip1"))); - AutofillScanner scanner(list_.get()); + AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); field_->AddClassifications(&field_candidates_map_); @@ -208,13 +216,14 @@ TEST_F(AddressFieldTest, ParseStateAndZipOneLabel) { field.label = ASCIIToUTF16("State/Province, Zip/Postal Code"); field.name = ASCIIToUTF16("state"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("state"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("state"))); field.label = ASCIIToUTF16("State/Province, Zip/Postal Code"); field.name = ASCIIToUTF16("zip"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("zip"))); + list_.push_back(base::MakeUnique<AutofillField>(field, ASCIIToUTF16("zip"))); - AutofillScanner scanner(list_.get()); + AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); field_->AddClassifications(&field_candidates_map_); @@ -234,9 +243,10 @@ TEST_F(AddressFieldTest, ParseCountry) { field.label = ASCIIToUTF16("Country"); field.name = ASCIIToUTF16("country"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("country1"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("country1"))); - AutofillScanner scanner(list_.get()); + AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); field_->AddClassifications(&field_candidates_map_); @@ -253,9 +263,10 @@ TEST_F(AddressFieldTest, ParseCompany) { field.label = ASCIIToUTF16("Company"); field.name = ASCIIToUTF16("company"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("company1"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("company1"))); - AutofillScanner scanner(list_.get()); + AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); field_->AddClassifications(&field_candidates_map_); diff --git a/chromium/components/autofill/core/browser/autofill_assistant.cc b/chromium/components/autofill/core/browser/autofill_assistant.cc index 4fccc2a7997..3e463b614bc 100644 --- a/chromium/components/autofill/core/browser/autofill_assistant.cc +++ b/chromium/components/autofill/core/browser/autofill_assistant.cc @@ -31,8 +31,7 @@ bool AutofillAssistant::CanShowCreditCardAssist( !IsAutofillCreditCardAssistEnabled() || // Context of the page is not secure or target URL is valid but not // secure. - !(autofill_manager_->client()->IsContextSecure( - form_structures.front()->source_url()) && + !(autofill_manager_->client()->IsContextSecure() && (!form_structures.front()->target_url().is_valid() || !form_structures.front()->target_url().SchemeIs("http")))) { return false; diff --git a/chromium/components/autofill/core/browser/autofill_client.h b/chromium/components/autofill/core/browser/autofill_client.h index 61b10177c41..3960f44fdbb 100644 --- a/chromium/components/autofill/core/browser/autofill_client.h +++ b/chromium/components/autofill/core/browser/autofill_client.h @@ -17,7 +17,6 @@ #include "ui/base/window_open_disposition.h" #include "url/gurl.h" -class GURL; class IdentityProvider; class PrefService; @@ -26,12 +25,11 @@ class RenderFrameHost; } namespace gfx { -class Rect; class RectF; } namespace rappor { -class RapporService; +class RapporServiceImpl; } namespace syncer { @@ -46,7 +44,6 @@ class CardUnmaskDelegate; class CreditCard; class FormStructure; class PersonalDataManager; -struct FormData; struct Suggestion; // A client interface that needs to be supplied to the Autofill component by the @@ -105,8 +102,8 @@ class AutofillClient { // Gets the IdentityProvider associated with the client (for OAuth2). virtual IdentityProvider* GetIdentityProvider() = 0; - // Gets the RapporService associated with the client (for metrics). - virtual rappor::RapporService* GetRapporService() = 0; + // Gets the RapporServiceImpl associated with the client (for metrics). + virtual rappor::RapporServiceImpl* GetRapporServiceImpl() = 0; // Causes the Autofill settings UI to be shown. virtual void ShowAutofillSettings() = 0; @@ -183,7 +180,7 @@ class AutofillClient { virtual void OnFirstUserGestureObserved() = 0; // If the context is secure. - virtual bool IsContextSecure(const GURL& form_origin) = 0; + virtual bool IsContextSecure() = 0; // Whether it is appropriate to show a signin promo for this user. virtual bool ShouldShowSigninPromo() = 0; @@ -191,6 +188,9 @@ class AutofillClient { // Starts the signin flow. Should not be called if ShouldShowSigninPromo() // returns false. virtual void StartSigninFlow() = 0; + + // Shows the explanation of http not secure warning message. + virtual void ShowHttpNotSecureExplanation() = 0; }; } // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_credit_card_filling_infobar_delegate_mobile.h b/chromium/components/autofill/core/browser/autofill_credit_card_filling_infobar_delegate_mobile.h index fe1cc199e0e..04e030694d5 100644 --- a/chromium/components/autofill/core/browser/autofill_credit_card_filling_infobar_delegate_mobile.h +++ b/chromium/components/autofill/core/browser/autofill_credit_card_filling_infobar_delegate_mobile.h @@ -13,10 +13,6 @@ #include "components/autofill/core/browser/autofill_metrics.h" #include "components/infobars/core/confirm_infobar_delegate.h" -namespace infobars { -class InfoBarManager; -} - namespace autofill { class CreditCard; diff --git a/chromium/components/autofill/core/browser/autofill_data_model.h b/chromium/components/autofill/core/browser/autofill_data_model.h index ee055ff49b3..4cc373b7177 100644 --- a/chromium/components/autofill/core/browser/autofill_data_model.h +++ b/chromium/components/autofill/core/browser/autofill_data_model.h @@ -15,8 +15,6 @@ namespace autofill { -class AutofillType; - // This class is an interface for the primary data models that back Autofill. // The information in objects of this class is managed by the // PersonalDataManager. diff --git a/chromium/components/autofill/core/browser/autofill_data_util.cc b/chromium/components/autofill/core/browser/autofill_data_util.cc index 5a09665034c..d931d7b0888 100644 --- a/chromium/components/autofill/core/browser/autofill_data_util.cc +++ b/chromium/components/autofill/core/browser/autofill_data_util.cc @@ -27,6 +27,7 @@ const PaymentRequestData kPaymentRequestData[]{ {"discoverCC", "discover", IDR_AUTOFILL_PR_DISCOVER}, {"jcbCC", "jcb", IDR_AUTOFILL_PR_JCB}, {"masterCardCC", "mastercard", IDR_AUTOFILL_PR_MASTERCARD}, + {"mirCC", "mir", IDR_AUTOFILL_PR_MIR}, {"unionPayCC", "unionpay", IDR_AUTOFILL_PR_UNIONPAY}, {"visaCC", "visa", IDR_AUTOFILL_PR_VISA}, }; diff --git a/chromium/components/autofill/core/browser/autofill_download_manager.cc b/chromium/components/autofill/core/browser/autofill_download_manager.cc index 331de998450..07084c52be4 100644 --- a/chromium/components/autofill/core/browser/autofill_download_manager.cc +++ b/chromium/components/autofill/core/browser/autofill_download_manager.cc @@ -247,8 +247,8 @@ bool AutofillDownloadManager::StartRequest( net::LOAD_DO_NOT_SEND_COOKIES); // Add Chrome experiment state to the request headers. net::HttpRequestHeaders headers; - // Note: It's fine to pass in |is_signed_in| false, which does not affect - // transmission of experiment ids coming from the variations server. + // Note: It's OK to pass |is_signed_in| false if it's unknown, as it does + // not affect transmission of experiments coming from the variations server. bool is_signed_in = false; variations::AppendVariationHeaders(fetcher->GetOriginalURL(), driver_->IsOffTheRecord(), false, 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 e9bfcb30cc9..73768ad2ace 100644 --- a/chromium/components/autofill/core/browser/autofill_download_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_download_manager_unittest.cc @@ -9,7 +9,9 @@ #include <list> #include <memory> #include <utility> +#include <vector> +#include "base/memory/ptr_util.h" #include "base/run_loop.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" @@ -49,6 +51,14 @@ void FakeOnURLFetchComplete(net::TestURLFetcher* fetcher, fetcher->delegate()->OnURLFetchComplete(fetcher); } +std::vector<FormStructure*> ToRawPointerVector( + const std::vector<std::unique_ptr<FormStructure>>& list) { + std::vector<FormStructure*> result; + for (const auto& item : list) + result.push_back(item.get()); + return result; +} + } // namespace // This tests AutofillDownloadManager. AutofillDownloadManagerTest implements @@ -166,9 +176,8 @@ TEST_F(AutofillDownloadManagerTest, QueryAndUploadTest) { field.form_control_type = "submit"; form.fields.push_back(field); - FormStructure* form_structure = new FormStructure(form); - ScopedVector<FormStructure> form_structures; - form_structures.push_back(form_structure); + std::vector<std::unique_ptr<FormStructure>> form_structures; + form_structures.push_back(base::MakeUnique<FormStructure>(form)); form.fields.clear(); @@ -192,8 +201,7 @@ TEST_F(AutofillDownloadManagerTest, QueryAndUploadTest) { field.form_control_type = "submit"; form.fields.push_back(field); - form_structure = new FormStructure(form); - form_structures.push_back(form_structure); + form_structures.push_back(base::MakeUnique<FormStructure>(form)); form.fields.clear(); @@ -212,12 +220,12 @@ TEST_F(AutofillDownloadManagerTest, QueryAndUploadTest) { field.form_control_type = "submit"; form.fields.push_back(field); - form_structure = new FormStructure(form); - form_structures.push_back(form_structure); + form_structures.push_back(base::MakeUnique<FormStructure>(form)); // Request with id 0. base::HistogramTester histogram; - EXPECT_TRUE(download_manager_.StartQueryRequest(form_structures.get())); + EXPECT_TRUE( + download_manager_.StartQueryRequest(ToRawPointerVector(form_structures))); histogram.ExpectUniqueSample("Autofill.ServerQueryResponse", AutofillMetrics::QUERY_SENT, 1); @@ -296,11 +304,11 @@ TEST_F(AutofillDownloadManagerTest, QueryAndUploadTest) { field.name = ASCIIToUTF16("address2"); field.form_control_type = "text"; form.fields.push_back(field); - form_structure = new FormStructure(form); - form_structures.push_back(form_structure); + form_structures.push_back(base::MakeUnique<FormStructure>(form)); // Request with id 4, not successful. - EXPECT_TRUE(download_manager_.StartQueryRequest(form_structures.get())); + EXPECT_TRUE( + download_manager_.StartQueryRequest(ToRawPointerVector(form_structures))); fetcher = factory.GetFetcherByID(4); ASSERT_TRUE(fetcher); histogram.ExpectUniqueSample("Autofill.ServerQueryResponse", @@ -343,13 +351,13 @@ TEST_F(AutofillDownloadManagerTest, BackoffLogic_Query) { field.form_control_type = "submit"; form.fields.push_back(field); - FormStructure* form_structure = new FormStructure(form); - ScopedVector<FormStructure> form_structures; - form_structures.push_back(form_structure); + std::vector<std::unique_ptr<FormStructure>> form_structures; + form_structures.push_back(base::MakeUnique<FormStructure>(form)); // Request with id 0. base::HistogramTester histogram; - EXPECT_TRUE(download_manager_.StartQueryRequest(form_structures.get())); + EXPECT_TRUE( + download_manager_.StartQueryRequest(ToRawPointerVector(form_structures))); histogram.ExpectUniqueSample("Autofill.ServerQueryResponse", AutofillMetrics::QUERY_SENT, 1); @@ -403,7 +411,7 @@ TEST_F(AutofillDownloadManagerTest, BackoffLogic_Upload) { field.form_control_type = "submit"; form.fields.push_back(field); - std::unique_ptr<FormStructure> form_structure(new FormStructure(form)); + auto form_structure = base::MakeUnique<FormStructure>(form); // Request with id 0. EXPECT_TRUE(download_manager_.StartUploadRequest( @@ -452,7 +460,7 @@ TEST_F(AutofillDownloadManagerTest, QueryTooManyFieldsTest) { // Create a query that contains too many fields for the server. std::vector<FormData> forms(21); - ScopedVector<FormStructure> form_structures; + std::vector<std::unique_ptr<FormStructure>> form_structures; for (auto& form : forms) { for (size_t i = 0; i < 5; ++i) { FormFieldData field; @@ -461,12 +469,12 @@ TEST_F(AutofillDownloadManagerTest, QueryTooManyFieldsTest) { field.form_control_type = "text"; form.fields.push_back(field); } - FormStructure* form_structure = new FormStructure(form); - form_structures.push_back(form_structure); + form_structures.push_back(base::MakeUnique<FormStructure>(form)); } // Check whether the query is aborted. - EXPECT_FALSE(download_manager_.StartQueryRequest(form_structures.get())); + EXPECT_FALSE( + download_manager_.StartQueryRequest(ToRawPointerVector(form_structures))); } TEST_F(AutofillDownloadManagerTest, QueryNotTooManyFieldsTest) { @@ -476,7 +484,7 @@ TEST_F(AutofillDownloadManagerTest, QueryNotTooManyFieldsTest) { // Create a query that contains a lot of fields, but not too many for the // server. std::vector<FormData> forms(25); - ScopedVector<FormStructure> form_structures; + std::vector<std::unique_ptr<FormStructure>> form_structures; for (auto& form : forms) { for (size_t i = 0; i < 4; ++i) { FormFieldData field; @@ -485,12 +493,12 @@ TEST_F(AutofillDownloadManagerTest, QueryNotTooManyFieldsTest) { field.form_control_type = "text"; form.fields.push_back(field); } - FormStructure* form_structure = new FormStructure(form); - form_structures.push_back(form_structure); + form_structures.push_back(base::MakeUnique<FormStructure>(form)); } // Check that the query is not aborted. - EXPECT_TRUE(download_manager_.StartQueryRequest(form_structures.get())); + EXPECT_TRUE( + download_manager_.StartQueryRequest(ToRawPointerVector(form_structures))); } TEST_F(AutofillDownloadManagerTest, CacheQueryTest) { @@ -514,26 +522,23 @@ TEST_F(AutofillDownloadManagerTest, CacheQueryTest) { field.name = ASCIIToUTF16("lastname"); form.fields.push_back(field); - FormStructure* form_structure = new FormStructure(form); - ScopedVector<FormStructure> form_structures0; - form_structures0.push_back(form_structure); + std::vector<std::unique_ptr<FormStructure>> form_structures0; + form_structures0.push_back(base::MakeUnique<FormStructure>(form)); // Add a slightly different form, which should result in a different request. field.label = ASCIIToUTF16("email"); field.name = ASCIIToUTF16("email"); form.fields.push_back(field); - form_structure = new FormStructure(form); - ScopedVector<FormStructure> form_structures1; - form_structures1.push_back(form_structure); + std::vector<std::unique_ptr<FormStructure>> form_structures1; + form_structures1.push_back(base::MakeUnique<FormStructure>(form)); // Add another slightly different form, which should also result in a // different request. field.label = ASCIIToUTF16("email2"); field.name = ASCIIToUTF16("email2"); form.fields.push_back(field); - form_structure = new FormStructure(form); - ScopedVector<FormStructure> form_structures2; - form_structures2.push_back(form_structure); + std::vector<std::unique_ptr<FormStructure>> form_structures2; + form_structures2.push_back(base::MakeUnique<FormStructure>(form)); // Limit cache to two forms. LimitCache(2); @@ -561,7 +566,8 @@ TEST_F(AutofillDownloadManagerTest, CacheQueryTest) { base::HistogramTester histogram; // Request with id 0. - EXPECT_TRUE(download_manager_.StartQueryRequest(form_structures0.get())); + EXPECT_TRUE(download_manager_.StartQueryRequest( + ToRawPointerVector(form_structures0))); histogram.ExpectUniqueSample("Autofill.ServerQueryResponse", AutofillMetrics::QUERY_SENT, 1); @@ -577,7 +583,8 @@ TEST_F(AutofillDownloadManagerTest, CacheQueryTest) { responses_.clear(); // No actual request - should be a cache hit. - EXPECT_TRUE(download_manager_.StartQueryRequest(form_structures0.get())); + EXPECT_TRUE(download_manager_.StartQueryRequest( + ToRawPointerVector(form_structures0))); histogram.ExpectUniqueSample("Autofill.ServerQueryResponse", AutofillMetrics::QUERY_SENT, 2); // Data is available immediately from cache - no over-the-wire trip. @@ -586,7 +593,8 @@ TEST_F(AutofillDownloadManagerTest, CacheQueryTest) { responses_.clear(); // Request with id 1. - EXPECT_TRUE(download_manager_.StartQueryRequest(form_structures1.get())); + EXPECT_TRUE(download_manager_.StartQueryRequest( + ToRawPointerVector(form_structures1))); histogram.ExpectUniqueSample("Autofill.ServerQueryResponse", AutofillMetrics::QUERY_SENT, 3); // No responses yet @@ -601,7 +609,8 @@ TEST_F(AutofillDownloadManagerTest, CacheQueryTest) { responses_.clear(); // Request with id 2. - EXPECT_TRUE(download_manager_.StartQueryRequest(form_structures2.get())); + EXPECT_TRUE(download_manager_.StartQueryRequest( + ToRawPointerVector(form_structures2))); histogram.ExpectUniqueSample("Autofill.ServerQueryResponse", AutofillMetrics::QUERY_SENT, 4); @@ -614,11 +623,13 @@ TEST_F(AutofillDownloadManagerTest, CacheQueryTest) { responses_.clear(); // No actual requests - should be a cache hit. - EXPECT_TRUE(download_manager_.StartQueryRequest(form_structures1.get())); + EXPECT_TRUE(download_manager_.StartQueryRequest( + ToRawPointerVector(form_structures1))); histogram.ExpectUniqueSample("Autofill.ServerQueryResponse", AutofillMetrics::QUERY_SENT, 5); - EXPECT_TRUE(download_manager_.StartQueryRequest(form_structures2.get())); + EXPECT_TRUE(download_manager_.StartQueryRequest( + ToRawPointerVector(form_structures2))); histogram.ExpectUniqueSample("Autofill.ServerQueryResponse", AutofillMetrics::QUERY_SENT, 6); @@ -629,7 +640,8 @@ TEST_F(AutofillDownloadManagerTest, CacheQueryTest) { // The first structure should've expired. // Request with id 3. - EXPECT_TRUE(download_manager_.StartQueryRequest(form_structures0.get())); + EXPECT_TRUE(download_manager_.StartQueryRequest( + ToRawPointerVector(form_structures0))); histogram.ExpectUniqueSample("Autofill.ServerQueryResponse", AutofillMetrics::QUERY_SENT, 7); // No responses yet diff --git a/chromium/components/autofill/core/browser/autofill_experiments.cc b/chromium/components/autofill/core/browser/autofill_experiments.cc index e066d854f55..39472262445 100644 --- a/chromium/components/autofill/core/browser/autofill_experiments.cc +++ b/chromium/components/autofill/core/browser/autofill_experiments.cc @@ -7,16 +7,20 @@ #include "base/command_line.h" #include "base/feature_list.h" #include "base/metrics/field_trial.h" +#include "base/strings/string16.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" #include "build/build_config.h" +#include "components/autofill/core/browser/suggestion.h" #include "components/autofill/core/common/autofill_pref_names.h" #include "components/autofill/core/common/autofill_switches.h" #include "components/prefs/pref_service.h" -#include "components/security_state/core/switches.h" #include "components/sync/driver/sync_service.h" #include "components/variations/variations_associated_data.h" #include "google_apis/gaia/gaia_auth_util.h" +#include "grit/components_strings.h" +#include "ui/base/l10n/l10n_util.h" namespace autofill { @@ -24,11 +28,37 @@ const base::Feature kAutofillCreditCardAssist{ "AutofillCreditCardAssist", base::FEATURE_DISABLED_BY_DEFAULT}; const base::Feature kAutofillCreditCardSigninPromo{ "AutofillCreditCardSigninPromo", base::FEATURE_DISABLED_BY_DEFAULT}; -const base::Feature kAutofillProfileCleanup{"AutofillProfileCleanup", - base::FEATURE_DISABLED_BY_DEFAULT}; const base::Feature kAutofillScanCardholderName{ "AutofillScanCardholderName", base::FEATURE_DISABLED_BY_DEFAULT}; +const base::Feature kAutofillCreditCardPopupLayout{ + "AutofillCreditCardPopupLayout", base::FEATURE_DISABLED_BY_DEFAULT}; const char kCreditCardSigninPromoImpressionLimitParamKey[] = "impression_limit"; +const char kAutofillCreditCardPopupBackgroundColorKey[] = "background_color"; +const char kAutofillCreditCardPopupDividerColorKey[] = "dropdown_divider_color"; +const char kAutofillCreditCardPopupValueBoldKey[] = "is_value_bold"; +const char kAutofillCreditCardPopupIsValueAndLabelInSingleLineKey[] = + "is_value_and_label_in_single_line"; +const char kAutofillPopupDropdownItemHeightKey[] = + "dropdown_item_height"; +const char kAutofillCreditCardPopupIsIconAtStartKey[] = + "is_credit_card_icon_at_start"; +const char kAutofillPopupMarginKey[] = "margin"; + +namespace { + +// Returns parameter value in |kAutofillCreditCardPopupLayout| feature, or 0 if +// parameter is not specified. +unsigned int GetCreditCardPopupParameterUintValue( + const std::string& param_name) { + unsigned int value; + const std::string param_value = variations::GetVariationParamValueByFeature( + kAutofillCreditCardPopupLayout, param_name); + if (!param_value.empty() && base::StringToUint(param_value, &value)) + return value; + return 0; +} + +} // namespace bool IsAutofillEnabled(const PrefService* pref_service) { return pref_service->GetBoolean(prefs::kAutofillEnabled); @@ -40,10 +70,6 @@ bool IsInAutofillSuggestionsDisabledExperiment() { return group_name == "Disabled"; } -bool IsAutofillProfileCleanupEnabled() { - return base::FeatureList::IsEnabled(kAutofillProfileCleanup); -} - bool IsAutofillCreditCardSigninPromoEnabled() { return base::FeatureList::IsEnabled(kAutofillCreditCardSigninPromo); } @@ -67,6 +93,64 @@ int GetCreditCardSigninPromoImpressionLimit() { return 0; } +bool IsAutofillCreditCardPopupLayoutExperimentEnabled() { + return base::FeatureList::IsEnabled(kAutofillCreditCardPopupLayout); +} + +// |GetCreditCardPopupParameterUintValue| returns 0 if experiment parameter is +// not specified. 0 == |SK_ColorTRANSPARENT|. +SkColor GetCreditCardPopupBackgroundColor() { + return GetCreditCardPopupParameterUintValue( + kAutofillCreditCardPopupBackgroundColorKey); +} + +SkColor GetCreditCardPopupDividerColor() { + return GetCreditCardPopupParameterUintValue( + kAutofillCreditCardPopupDividerColorKey); +} + +bool IsCreditCardPopupValueBold() { + const std::string param_value = variations::GetVariationParamValueByFeature( + kAutofillCreditCardPopupLayout, kAutofillCreditCardPopupValueBoldKey); + return param_value == "true"; +} + +unsigned int GetPopupDropdownItemHeight() { + return GetCreditCardPopupParameterUintValue( + kAutofillPopupDropdownItemHeightKey); +} + +bool IsIconInCreditCardPopupAtStart() { + const std::string param_value = variations::GetVariationParamValueByFeature( + kAutofillCreditCardPopupLayout, kAutofillCreditCardPopupIsIconAtStartKey); + return param_value == "true"; +} + +// Modifies |suggestion| as follows if experiment to display value and label in +// a single line is enabled. +// Say, |value| is 'Visa ....1111' and |label| is '01/18' (expiration date). +// Modifies |value| to 'Visa ....1111, exp 01/18' and clears |label|. +void ModifyAutofillCreditCardSuggestion(Suggestion* suggestion) { + DCHECK(IsAutofillCreditCardPopupLayoutExperimentEnabled()); + const std::string param_value = variations::GetVariationParamValueByFeature( + kAutofillCreditCardPopupLayout, + kAutofillCreditCardPopupIsValueAndLabelInSingleLineKey); + if (param_value == "true") { + const base::string16 format_string = l10n_util::GetStringUTF16( + IDS_AUTOFILL_CREDIT_CARD_EXPIRATION_DATE_LABEL_AND_ABBR); + if (!format_string.empty()) { + suggestion->value.append(l10n_util::GetStringFUTF16( + IDS_AUTOFILL_CREDIT_CARD_EXPIRATION_DATE_LABEL_AND_ABBR, + suggestion->label)); + } + suggestion->label.clear(); + } +} + +unsigned int GetPopupMargin() { + return GetCreditCardPopupParameterUintValue(kAutofillPopupMarginKey); +} + bool OfferStoreUnmaskedCards() { #if defined(OS_LINUX) && !defined(OS_CHROMEOS) // The checkbox can be forced on with a flag, but by default we don't store @@ -108,9 +192,9 @@ bool IsCreditCardUploadEnabled(const PrefService* pref_service, // Users who have enabled a passphrase have chosen to not make their sync // information accessible to Google. Since upload makes credit card data // available to other Google systems, disable it for passphrase users. - // We can't determine the passphrase state until the sync backend is + // We can't determine the passphrase state until the sync engine is // initialized so disable upload if sync is not yet available. - if (!sync_service->IsBackendInitialized() || + if (!sync_service->IsEngineInitialized() || sync_service->IsUsingSecondaryPassphrase()) { return false; } @@ -142,12 +226,4 @@ bool IsCreditCardUploadEnabled(const PrefService* pref_service, return !group_name.empty() && group_name != "Disabled"; } -bool IsCreditCardAutofillHttpWarningEnabled() { - std::string choice = - base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( - security_state::switches::kMarkHttpAs); - return choice == security_state::switches:: - kMarkHttpWithPasswordsOrCcWithChipAndFormWarning; -} - } // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_experiments.h b/chromium/components/autofill/core/browser/autofill_experiments.h index 9fe4fc0b556..4467e38d9b3 100644 --- a/chromium/components/autofill/core/browser/autofill_experiments.h +++ b/chromium/components/autofill/core/browser/autofill_experiments.h @@ -7,6 +7,9 @@ #include <string> +#include "base/strings/string16.h" +#include "third_party/skia/include/core/SkColor.h" + class PrefService; namespace base { @@ -19,11 +22,14 @@ class SyncService; namespace autofill { +struct Suggestion; + extern const base::Feature kAutofillCreditCardAssist; extern const base::Feature kAutofillCreditCardSigninPromo; -extern const base::Feature kAutofillProfileCleanup; extern const base::Feature kAutofillScanCardholderName; +extern const base::Feature kAutofillCreditCardPopupLayout; extern const char kCreditCardSigninPromoImpressionLimitParamKey[]; +extern const char kAutofillCreditCardPopupSettingsSuggestionValueKey[]; // Returns true if autofill should be enabled. See also // IsInAutofillSuggestionsDisabledExperiment below. @@ -35,9 +41,6 @@ bool IsAutofillEnabled(const PrefService* pref_service); // disables providing suggestions. bool IsInAutofillSuggestionsDisabledExperiment(); -// Returns whether the Autofill profile cleanup feature is enabled. -bool IsAutofillProfileCleanupEnabled(); - // Returns whether the Autofill credit card signin promo should be shown. bool IsAutofillCreditCardSigninPromoEnabled(); @@ -60,9 +63,41 @@ bool IsCreditCardUploadEnabled(const PrefService* pref_service, const syncer::SyncService* sync_service, const std::string& user_email); -// Returns true if the http warning switch is on, which will display a warning -// in the autofill dropdown when credit card fields are on an HTTP page. -bool IsCreditCardAutofillHttpWarningEnabled(); +// Returns whether the new Autofill credit card popup layout experiment is +// enabled. +bool IsAutofillCreditCardPopupLayoutExperimentEnabled(); + +// Returns the background color for credit card autofill popup, or +// |SK_ColorTRANSPARENT| if the new credit card autofill popup layout experiment +// is not enabled. +SkColor GetCreditCardPopupBackgroundColor(); + +// Returns the divider color for credit card autofill popup, or +// |SK_ColorTRANSPARENT| if the new credit card autofill popup layout experiment +// is not enabled. +SkColor GetCreditCardPopupDividerColor(); + +// Returns true if the credit card autofill popup suggestion value is displayed +// in bold type face. +bool IsCreditCardPopupValueBold(); + +// Returns the dropdown item height for autofill popup, returning 0 if the +// dropdown item height isn't configured in an experiment to tweak autofill +// popup layout. +unsigned int GetPopupDropdownItemHeight(); + +// Returns true if the icon in the credit card autofill popup must be displayed +// before the credit card value or any other suggestion text. +bool IsIconInCreditCardPopupAtStart(); + +// Modifies the suggestion value and label if the new credit card autofill popup +// experiment is enabled to tweak the display of the value and label. +void ModifyAutofillCreditCardSuggestion(struct Suggestion* suggestion); + +// Returns the margin for the icon, label and between icon and label. Returns 0 +// if the margin isn't configured in an experiment to tweak autofill popup +// layout. +unsigned int GetPopupMargin(); } // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_external_delegate.cc b/chromium/components/autofill/core/browser/autofill_external_delegate.cc index 96a96c34879..e80c8ac5461 100644 --- a/chromium/components/autofill/core/browser/autofill_external_delegate.cc +++ b/chromium/components/autofill/core/browser/autofill_external_delegate.cc @@ -19,6 +19,7 @@ #include "build/build_config.h" #include "components/autofill/core/browser/autocomplete_history_manager.h" #include "components/autofill/core/browser/autofill_driver.h" +#include "components/autofill/core/browser/autofill_experiments.h" #include "components/autofill/core/browser/autofill_manager.h" #include "components/autofill/core/browser/autofill_metrics.h" #include "components/autofill/core/browser/popup_item_ids.h" @@ -31,9 +32,11 @@ namespace autofill { namespace { // Returns true if the suggestion entry is an Autofill warning message. -// Warning message should display on top of suggestion list. +// Warning messages should display on top of suggestion list. bool IsAutofillWarningEntry(int frontend_id) { - return frontend_id == POPUP_ITEM_ID_WARNING_MESSAGE; + return frontend_id == + POPUP_ITEM_ID_INSECURE_CONTEXT_PAYMENT_DISABLED_MESSAGE || + frontend_id == POPUP_ITEM_ID_HTTP_NOT_SECURE_WARNING_MESSAGE; } } // anonymous namespace @@ -46,6 +49,7 @@ AutofillExternalDelegate::AutofillExternalDelegate(AutofillManager* manager, has_autofill_suggestions_(false), has_shown_popup_for_current_edit_(false), should_show_scan_credit_card_(false), + is_credit_card_popup_(false), should_show_cc_signin_promo_(false), has_shown_address_book_prompt(false), weak_ptr_factory_(this) { @@ -67,6 +71,8 @@ void AutofillExternalDelegate::OnQuery(int query_id, element_bounds_ = element_bounds; should_show_scan_credit_card_ = manager_->ShouldShowScanCreditCard(query_form_, query_field_); + is_credit_card_popup_ = + manager_->IsCreditCardPopup(query_form_, query_field_); should_show_cc_signin_promo_ = manager_->ShouldShowCreditCardSigninPromo(query_form_, query_field_); } @@ -210,7 +216,8 @@ void AutofillExternalDelegate::DidAcceptSuggestion(const base::string16& value, } else if (identifier == POPUP_ITEM_ID_CLEAR_FORM) { // User selected 'Clear form'. driver_->RendererShouldClearFilledForm(); - } else if (identifier == POPUP_ITEM_ID_PASSWORD_ENTRY) { + } else if (identifier == POPUP_ITEM_ID_PASSWORD_ENTRY || + identifier == POPUP_ITEM_ID_USERNAME_ENTRY) { NOTREACHED(); // Should be handled elsewhere. } else if (identifier == POPUP_ITEM_ID_DATALIST_ENTRY) { driver_->RendererShouldAcceptDataListSuggestion(value); @@ -223,6 +230,9 @@ void AutofillExternalDelegate::DidAcceptSuggestion(const base::string16& value, &AutofillExternalDelegate::OnCreditCardScanned, GetWeakPtr())); } else if (identifier == POPUP_ITEM_ID_CREDIT_CARD_SIGNIN_PROMO) { manager_->client()->StartSigninFlow(); + } else if (identifier == POPUP_ITEM_ID_HTTP_NOT_SECURE_WARNING_MESSAGE) { + AutofillMetrics::LogShowedHttpNotSecureExplanation(); + manager_->client()->ShowHttpNotSecureExplanation(); } else { if (identifier > 0) // Denotes an Autofill suggestion. AutofillMetrics::LogAutofillSuggestionAcceptedIndex(position); @@ -271,14 +281,12 @@ void AutofillExternalDelegate::ClearPreviewedForm() { driver_->RendererShouldClearPreviewedForm(); } -void AutofillExternalDelegate::Reset() { - manager_->client()->HideAutofillPopup(); +bool AutofillExternalDelegate::IsCreditCardPopup() { + return is_credit_card_popup_; } -void AutofillExternalDelegate::OnPingAck() { - // Reissue the most recent query, which will reopen the Autofill popup. - manager_->OnQueryFormFieldAutofill(query_id_, query_form_, query_field_, - element_bounds_); +void AutofillExternalDelegate::Reset() { + manager_->client()->HideAutofillPopup(); } base::WeakPtr<AutofillExternalDelegate> AutofillExternalDelegate::GetWeakPtr() { @@ -293,7 +301,7 @@ void AutofillExternalDelegate::OnCreditCardScanned(const CreditCard& card) { void AutofillExternalDelegate::FillAutofillFormData(int unique_id, bool is_preview) { // If the selected element is a warning we don't want to do anything. - if (unique_id == POPUP_ITEM_ID_WARNING_MESSAGE) + if (IsAutofillWarningEntry(unique_id)) return; AutofillDriver::RendererFormDataAction renderer_action = is_preview ? @@ -327,8 +335,6 @@ void AutofillExternalDelegate::ApplyAutofillOptions( if (query_field_.is_autofilled) { base::string16 value = l10n_util::GetStringUTF16(IDS_AUTOFILL_CLEAR_FORM_MENU_ITEM); - // TODO(rouslan): Remove manual upper-casing when keyboard accessory becomes - // default on Android. if (IsKeyboardAccessoryEnabled()) value = base::i18n::ToUpper(value); @@ -336,12 +342,9 @@ void AutofillExternalDelegate::ApplyAutofillOptions( suggestions->back().frontend_id = POPUP_ITEM_ID_CLEAR_FORM; } - // Append the 'Chrome Autofill options' menu item; - // TODO(rouslan): Switch on the platform in the GRD file when keyboard - // accessory becomes default on Android. - suggestions->push_back(Suggestion(l10n_util::GetStringUTF16( - IsKeyboardAccessoryEnabled() ? IDS_AUTOFILL_OPTIONS_CONTENT_DESCRIPTION - : IDS_AUTOFILL_OPTIONS_POPUP))); + // Append the 'Chrome Autofill options' menu item, or the menu item specified + // in the popup layout experiment. + suggestions->push_back(Suggestion(GetSettingsSuggestionValue())); suggestions->back().frontend_id = POPUP_ITEM_ID_AUTOFILL_OPTIONS; if (IsKeyboardAccessoryEnabled()) suggestions->back().icon = base::ASCIIToUTF16("settings"); @@ -384,4 +387,14 @@ void AutofillExternalDelegate::InsertDataListValues( } } +base::string16 AutofillExternalDelegate::GetSettingsSuggestionValue() + const { + if (IsKeyboardAccessoryEnabled()) { + return l10n_util::GetStringUTF16(IDS_AUTOFILL_OPTIONS_CONTENT_DESCRIPTION); + } + return l10n_util::GetStringUTF16(is_credit_card_popup_ ? + IDS_AUTOFILL_CREDIT_CARD_OPTIONS_POPUP : + IDS_AUTOFILL_OPTIONS_POPUP); +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_external_delegate.h b/chromium/components/autofill/core/browser/autofill_external_delegate.h index 5431b31fe6c..6a9807a0505 100644 --- a/chromium/components/autofill/core/browser/autofill_external_delegate.h +++ b/chromium/components/autofill/core/browser/autofill_external_delegate.h @@ -51,6 +51,9 @@ class AutofillExternalDelegate : public AutofillPopupDelegate { base::string16* body) override; bool RemoveSuggestion(const base::string16& value, int identifier) override; void ClearPreviewedForm() override; + // Returns false for all popups prior to |onQuery|, true for credit card + // popups after call to |onQuery|. + bool IsCreditCardPopup() override; // Records and associates a query_id with web form data. Called // when the renderer posts an Autofill query to the browser. |bounds| @@ -81,9 +84,6 @@ class AutofillExternalDelegate : public AutofillPopupDelegate { // values or settings. void Reset(); - // The renderer sent an IPC acknowledging an earlier ping IPC. - void OnPingAck(); - protected: base::WeakPtr<AutofillExternalDelegate> GetWeakPtr(); @@ -118,6 +118,9 @@ class AutofillExternalDelegate : public AutofillPopupDelegate { // version. void InsertDataListValues(std::vector<Suggestion>* suggestions); + // Returns the text (i.e. |Suggestion| value) for Chrome autofill options. + base::string16 GetSettingsSuggestionValue() const; + AutofillManager* manager_; // weak. // Provides driver-level context to the shared code of the component. Must @@ -142,8 +145,8 @@ class AutofillExternalDelegate : public AutofillPopupDelegate { // currently editing? Used to keep track of state for metrics logging. bool has_shown_popup_for_current_edit_; - // FIXME bool should_show_scan_credit_card_; + bool is_credit_card_popup_; // Whether the credit card signin promo should be shown to the user. bool should_show_cc_signin_promo_; 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 709c73444dd..97bd64b385c 100644 --- a/chromium/components/autofill/core/browser/autofill_external_delegate_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_external_delegate_unittest.cc @@ -80,6 +80,8 @@ class MockAutofillClient : public TestAutofillClient { MOCK_METHOD0(StartSigninFlow, void()); + MOCK_METHOD0(ShowHttpNotSecureExplanation, void()); + private: DISALLOW_COPY_AND_ASSIGN(MockAutofillClient); }; @@ -463,16 +465,15 @@ TEST_F(AutofillExternalDelegateUnitTest, AutofillWarnings) { EXPECT_CALL( autofill_client_, ShowAutofillPopup( - _, - _, - SuggestionVectorIdsAre(testing::ElementsAre( - static_cast<int>(POPUP_ITEM_ID_WARNING_MESSAGE))), + _, _, SuggestionVectorIdsAre(testing::ElementsAre(static_cast<int>( + POPUP_ITEM_ID_INSECURE_CONTEXT_PAYMENT_DISABLED_MESSAGE))), _)); // This should call ShowAutofillPopup. std::vector<Suggestion> autofill_item; autofill_item.push_back(Suggestion()); - autofill_item[0].frontend_id = POPUP_ITEM_ID_WARNING_MESSAGE; + autofill_item[0].frontend_id = + POPUP_ITEM_ID_INSECURE_CONTEXT_PAYMENT_DISABLED_MESSAGE; external_delegate_->OnSuggestionsReturned(kQueryId, autofill_item); } @@ -493,9 +494,10 @@ TEST_F(AutofillExternalDelegateUnitTest, // This should call ShowAutofillPopup. std::vector<Suggestion> suggestions; suggestions.push_back(Suggestion()); - suggestions[0].frontend_id = POPUP_ITEM_ID_WARNING_MESSAGE; + suggestions[0].frontend_id = POPUP_ITEM_ID_HTTP_NOT_SECURE_WARNING_MESSAGE; suggestions.push_back(Suggestion()); - suggestions[1].frontend_id = POPUP_ITEM_ID_WARNING_MESSAGE; + suggestions[1].frontend_id = + POPUP_ITEM_ID_INSECURE_CONTEXT_PAYMENT_DISABLED_MESSAGE; suggestions.push_back(Suggestion()); suggestions[2].value = ASCIIToUTF16("Rick"); suggestions[2].frontend_id = POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY; @@ -671,6 +673,15 @@ TEST_F(AutofillExternalDelegateUnitTest, SigninPromoMenuItem) { base::string16(), POPUP_ITEM_ID_CREDIT_CARD_SIGNIN_PROMO, 0); } +// Test that autofill client will open the security indicator help center url +// after the user accepted the http warning message suggestion item. +TEST_F(AutofillExternalDelegateUnitTest, HttpWarningMessageItem) { + EXPECT_CALL(autofill_client_, ShowHttpNotSecureExplanation()); + EXPECT_CALL(autofill_client_, HideAutofillPopup()); + external_delegate_->DidAcceptSuggestion( + base::string16(), POPUP_ITEM_ID_HTTP_NOT_SECURE_WARNING_MESSAGE, 0); +} + MATCHER_P(CreditCardMatches, card, "") { return !arg.Compare(card); } diff --git a/chromium/components/autofill/core/browser/autofill_manager.cc b/chromium/components/autofill/core/browser/autofill_manager.cc index 7923e0bc9d5..977e67ae874 100644 --- a/chromium/components/autofill/core/browser/autofill_manager.cc +++ b/chromium/components/autofill/core/browser/autofill_manager.cc @@ -50,6 +50,7 @@ #include "components/autofill/core/browser/phone_number.h" #include "components/autofill/core/browser/phone_number_i18n.h" #include "components/autofill/core/browser/popup_item_ids.h" +#include "components/autofill/core/browser/validation.h" #include "components/autofill/core/common/autofill_constants.h" #include "components/autofill/core/common/autofill_data_validation.h" #include "components/autofill/core/common/autofill_pref_names.h" @@ -60,7 +61,9 @@ #include "components/autofill/core/common/password_form_fill_data.h" #include "components/pref_registry/pref_registry_syncable.h" #include "components/prefs/pref_service.h" -#include "components/rappor/rappor_utils.h" +#include "components/rappor/public/rappor_utils.h" +#include "components/rappor/rappor_service_impl.h" +#include "components/security_state/core/security_state.h" #include "google_apis/gaia/identity_provider.h" #include "grit/components_strings.h" #include "ui/base/l10n/l10n_util.h" @@ -183,6 +186,23 @@ bool IsCreditCardExpirationType(ServerFieldType type) { type == CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR; } +// Create http bad warning message at the top of autofill popup list showing +// "Payment not secure" when users are on http sites or broken https sites. +Suggestion CreateHttpWarningMessageSuggestionItem(const GURL& source_url) { + Suggestion cc_field_http_warning_suggestion( + l10n_util::GetStringUTF16(IDS_AUTOFILL_CREDIT_CARD_HTTP_WARNING_MESSAGE)); + cc_field_http_warning_suggestion.frontend_id = + POPUP_ITEM_ID_HTTP_NOT_SECURE_WARNING_MESSAGE; + cc_field_http_warning_suggestion.label = + l10n_util::GetStringUTF16(IDS_AUTOFILL_HTTP_WARNING_LEARN_MORE); + cc_field_http_warning_suggestion.icon = + (source_url.is_valid() && source_url.SchemeIs("http")) + ? base::ASCIIToUTF16("httpWarning") + : base::ASCIIToUTF16("httpsInvalid"); + + return cc_field_http_warning_suggestion; +} + } // namespace AutofillManager::AutofillManager( @@ -290,6 +310,12 @@ bool AutofillManager::ShouldShowScanCreditCard(const FormData& form, return field.value.size() <= kShowScanCreditCardMaxValueLength; } +bool AutofillManager::IsCreditCardPopup(const FormData& form, + const FormFieldData& field) { + AutofillField* autofill_field = GetAutofillField(form, field); + return autofill_field && autofill_field->Type().group() == CREDIT_CARD; +} + bool AutofillManager::ShouldShowCreditCardSigninPromo( const FormData& form, const FormFieldData& field) { @@ -510,8 +536,6 @@ void AutofillManager::OnQueryFormFieldAutofill(int query_id, if (!IsValidFormData(form) || !IsValidFormFieldData(field)) return; - std::vector<Suggestion> suggestions; - gfx::RectF transformed_box = driver_->TransformBoundingBoxToViewportCoordinates(bounding_box); external_delegate_->OnQuery(query_id, form, field, transformed_box); @@ -526,9 +550,12 @@ void AutofillManager::OnQueryFormFieldAutofill(int query_id, // Don't send suggestions or track forms that should not be parsed. form_structure->ShouldBeParsed(); - // Logging interactions of forms that are autofillable. + bool is_filling_credit_card = false; + + // Log interactions of forms that are autofillable. if (got_autofillable_form) { if (autofill_field->Type().group() == CREDIT_CARD) { + is_filling_credit_card = true; driver_->DidInteractWithCreditCardForm(); credit_card_form_event_logger_->OnDidInteractWithAutofillableForm(); } else { @@ -536,11 +563,18 @@ void AutofillManager::OnQueryFormFieldAutofill(int query_id, } } + std::vector<Suggestion> suggestions; + const bool is_context_secure = + !form_structure || + (client_->IsContextSecure() && + (!form_structure->target_url().is_valid() || + !form_structure->target_url().SchemeIs("http"))); + const bool is_http_warning_enabled = + security_state::IsHttpWarningInFormEnabled(); + if (is_autofill_possible && driver_->RendererIsAvailable() && got_autofillable_form) { - AutofillType type = autofill_field->Type(); - bool is_filling_credit_card = (type.group() == CREDIT_CARD); // On desktop, don't return non credit card related suggestions for forms or // fields that have the "autocomplete" attribute set to off. if (IsDesktopPlatform() && !is_filling_credit_card && @@ -548,16 +582,13 @@ void AutofillManager::OnQueryFormFieldAutofill(int query_id, return; } if (is_filling_credit_card) { - suggestions = GetCreditCardSuggestions(field, type); + suggestions = GetCreditCardSuggestions(field, autofill_field->Type()); } else { suggestions = GetProfileSuggestions(*form_structure, field, *autofill_field); } + if (!suggestions.empty()) { - bool is_context_secure = - client_->IsContextSecure(form_structure->source_url()) && - (!form_structure->target_url().is_valid() || - !form_structure->target_url().SchemeIs("http")); if (is_filling_credit_card) AutofillMetrics::LogIsQueriedCreditCardFormSecure(is_context_secure); @@ -566,26 +597,18 @@ void AutofillManager::OnQueryFormFieldAutofill(int query_id, // IsContextSecure). if (is_filling_credit_card && !is_context_secure) { // Replace the suggestion content with a warning message explaining why - // Autofill is disabled for a website. + // Autofill is disabled for a website. The string is different if the + // credit card autofill HTTP warning experiment is enabled. Suggestion warning_suggestion(l10n_util::GetStringUTF16( - IDS_AUTOFILL_WARNING_INSECURE_CONNECTION)); - warning_suggestion.frontend_id = POPUP_ITEM_ID_WARNING_MESSAGE; + is_http_warning_enabled + ? IDS_AUTOFILL_WARNING_PAYMENT_DISABLED + : IDS_AUTOFILL_WARNING_INSECURE_CONNECTION)); + warning_suggestion.frontend_id = + POPUP_ITEM_ID_INSECURE_CONTEXT_PAYMENT_DISABLED_MESSAGE; suggestions.assign(1, warning_suggestion); - - // On top of the explanation message, first show a "Payment not secure" - // message. - if (IsCreditCardAutofillHttpWarningEnabled()) { - Suggestion cc_field_http_warning_suggestion(l10n_util::GetStringUTF16( - IDS_AUTOFILL_CREDIT_CARD_HTTP_WARNING_MESSAGE)); - cc_field_http_warning_suggestion.frontend_id = - POPUP_ITEM_ID_WARNING_MESSAGE; - suggestions.insert(suggestions.begin(), - cc_field_http_warning_suggestion); - } } else { - bool section_is_autofilled = - SectionIsAutofilled(*form_structure, form, - autofill_field->section()); + bool section_is_autofilled = SectionIsAutofilled( + *form_structure, form, autofill_field->section()); if (section_is_autofilled) { // If the relevant section is auto-filled and the renderer is querying // for suggestions, then the user is editing the value of a field. @@ -617,6 +640,21 @@ void AutofillManager::OnQueryFormFieldAutofill(int query_id, } } + // Show a "Payment not secure" message. + if (!is_context_secure && is_filling_credit_card && is_http_warning_enabled) { +#if !defined(OS_ANDROID) + if (!suggestions.empty()) { + suggestions.insert(suggestions.begin(), Suggestion()); + suggestions.front().frontend_id = POPUP_ITEM_ID_SEPARATOR; + } +#endif + + suggestions.insert( + suggestions.begin(), + CreateHttpWarningMessageSuggestionItem( + form_structure ? form_structure->source_url() : GURL::EmptyGURL())); + } + // If there are no Autofill suggestions, consider showing Autocomplete // suggestions. We will not show Autocomplete suggestions for a field that // specifies autocomplete=off (or an unrecognized type), a field for which we @@ -948,7 +986,7 @@ void AutofillManager::OnLoadedServerPredictions( // Parse and store the server predictions. FormStructure::ParseQueryResponse(std::move(response), queried_forms, - client_->GetRapporService()); + client_->GetRapporServiceImpl()); // Will log quality metrics for each FormStructure based on the presence of // autocomplete attributes, if available. @@ -1091,7 +1129,7 @@ void AutofillManager::ImportFormData(const FormStructure& submitted_form) { recently_autofilled_forms_.push_back( std::map<std::string, base::string16>()); auto& map = recently_autofilled_forms_.back(); - for (const auto* field : submitted_form) { + for (const auto& field : submitted_form) { AutofillType type = field->Type(); // Even though this is for development only, mask full credit card #'s. if (type.GetStorableType() == CREDIT_CARD_NUMBER && @@ -1131,10 +1169,10 @@ void AutofillManager::ImportFormData(const FormStructure& submitted_form) { // their card. If no CVC is present, do nothing. We could fall back to a // local save but we believe that sometimes offering upload and sometimes // offering local save is a confusing user experience. - int cvc; - for (const AutofillField* field : submitted_form) { + for (const auto& field : submitted_form) { if (field->Type().GetStorableType() == CREDIT_CARD_VERIFICATION_CODE && - base::StringToInt(field->value, &cvc)) { + IsValidCreditCardSecurityCode(field->value, + upload_request_.card.type())) { upload_request_.cvc = field->value; break; } @@ -1268,8 +1306,8 @@ bool AutofillManager::GetProfilesForCreditCardUpload( void AutofillManager::CollectRapportSample(const GURL& source_url, const char* metric_name) const { - if (source_url.is_valid() && client_->GetRapporService()) { - rappor::SampleDomainAndRegistryFromGURL(client_->GetRapporService(), + if (source_url.is_valid() && client_->GetRapporServiceImpl()) { + rappor::SampleDomainAndRegistryFromGURL(client_->GetRapporServiceImpl(), metric_name, source_url); } } @@ -1284,9 +1322,10 @@ void AutofillManager::UploadFormDataAsyncCallback( const TimeTicks& interaction_time, const TimeTicks& submission_time, bool observed_submission) { - submitted_form->LogQualityMetrics( - load_time, interaction_time, submission_time, client_->GetRapporService(), - did_show_suggestions_, observed_submission); + submitted_form->LogQualityMetrics(load_time, interaction_time, + submission_time, + client_->GetRapporServiceImpl(), + did_show_suggestions_, observed_submission); if (submitted_form->ShouldBeCrowdsourced()) UploadFormData(*submitted_form, observed_submission); @@ -1400,6 +1439,8 @@ bool AutofillManager::RefreshDataModels() { is_server_data_available); credit_card_form_event_logger_->set_is_local_data_available( is_local_data_available); + credit_card_form_event_logger_->set_is_context_secure( + client_->IsContextSecure()); } { bool is_server_data_available = false; @@ -1647,9 +1688,9 @@ bool AutofillManager::GetCachedFormAndField(const FormData& form, // Find the AutofillField that corresponds to |field|. *autofill_field = NULL; - for (AutofillField* current : **form_structure) { + for (const auto& current : **form_structure) { if (current->SameFieldAs(field)) { - *autofill_field = current; + *autofill_field = current.get(); break; } } @@ -1769,6 +1810,7 @@ std::vector<Suggestion> AutofillManager::GetCreditCardSuggestions( for (size_t i = 0; i < suggestions.size(); i++) { suggestions[i].frontend_id = MakeFrontendID(suggestions[i].backend_id, std::string()); + suggestions[i].is_value_bold = IsCreditCardPopupValueBold(); } return suggestions; } diff --git a/chromium/components/autofill/core/browser/autofill_manager.h b/chromium/components/autofill/core/browser/autofill_manager.h index f3d3e2f762e..ce8608f03db 100644 --- a/chromium/components/autofill/core/browser/autofill_manager.h +++ b/chromium/components/autofill/core/browser/autofill_manager.h @@ -44,7 +44,6 @@ #endif namespace gfx { -class Rect; class RectF; } @@ -97,6 +96,10 @@ class AutofillManager : public AutofillDownloadManager::Observer, virtual bool ShouldShowScanCreditCard(const FormData& form, const FormFieldData& field); + // Whether the |field| belongs to CREDIT_CARD |FieldTypeGroup|. + virtual bool IsCreditCardPopup(const FormData& form, + const FormFieldData& field); + // Whether we should show the signin promo, based on the triggered |field| // inside the |form|. virtual bool ShouldShowCreditCardSigninPromo(const FormData& form, diff --git a/chromium/components/autofill/core/browser/autofill_manager_unittest.cc b/chromium/components/autofill/core/browser/autofill_manager_unittest.cc index eea68f0a4b9..38264eda18c 100644 --- a/chromium/components/autofill/core/browser/autofill_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_manager_unittest.cc @@ -49,7 +49,7 @@ #include "components/autofill/core/common/form_field_data.h" #include "components/prefs/pref_service.h" #include "components/rappor/test_rappor_service.h" -#include "components/security_state/core/switches.h" +#include "components/security_state/core/security_state.h" #include "components/variations/variations_associated_data.h" #include "grit/components_strings.h" #include "net/url_request/url_request_test_util.h" @@ -72,6 +72,13 @@ namespace { const int kDefaultPageID = 137; +const std::string kUTF8MidlineEllipsis = + " " + "\xE2\x80\xA2\xE2\x80\x86" + "\xE2\x80\xA2\xE2\x80\x86" + "\xE2\x80\xA2\xE2\x80\x86" + "\xE2\x80\xA2\xE2\x80\x86"; + class MockAutofillClient : public TestAutofillClient { public: MockAutofillClient() {} @@ -688,7 +695,7 @@ class TestAutofillExternalDelegate : public AutofillExternalDelegate { EXPECT_TRUE(on_suggestions_returned_seen_); EXPECT_EQ(expected_page_id, query_id_); - ASSERT_EQ(expected_num_suggestions, suggestions_.size()); + ASSERT_LE(expected_num_suggestions, suggestions_.size()); for (size_t i = 0; i < expected_num_suggestions; ++i) { SCOPED_TRACE(base::StringPrintf("i: %" PRIuS, i)); EXPECT_EQ(expected_suggestions[i].value, suggestions_[i].value); @@ -697,6 +704,7 @@ class TestAutofillExternalDelegate : public AutofillExternalDelegate { EXPECT_EQ(expected_suggestions[i].frontend_id, suggestions_[i].frontend_id); } + ASSERT_EQ(expected_num_suggestions, suggestions_.size()); } // Wrappers around the above GetSuggestions call that take a hardcoded number @@ -1012,10 +1020,8 @@ class AutofillManagerTest : public testing::Test { } void SetHttpWarningEnabled() { - base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( - security_state::switches::kMarkHttpAs, - security_state::switches:: - kMarkHttpWithPasswordsOrCcWithChipAndFormWarning); + scoped_feature_list_.InitAndEnableFeature( + security_state::kHttpFormWarningFeature); } protected: @@ -1265,6 +1271,24 @@ TEST_F(AutofillManagerTest, GetProfileSuggestions_EmptyValue) { Suggestion("Elvis", "3734 Elvis Presley Blvd.", "", 2)); } +// Test that the HttpWarning does not appear on non-payment forms. +TEST_F(AutofillManagerTest, GetProfileSuggestions_EmptyValueNotSecure) { + SetHttpWarningEnabled(); + // Set up our form data. + FormData form; + test::CreateTestAddressFormData(&form); + std::vector<FormData> forms(1, form); + FormsSeen(forms); + + const FormFieldData& field = form.fields[0]; + GetAutofillSuggestions(form, field); + + // Test that we sent the right values to the external delegate. + external_delegate_->CheckSuggestions( + kDefaultPageID, Suggestion("Charles", "123 Apple St.", "", 1), + Suggestion("Elvis", "3734 Elvis Presley Blvd.", "", 2)); +} + // Test that we return only matching address profile suggestions when the // selected form field has been partially filled out. TEST_F(AutofillManagerTest, GetProfileSuggestions_MatchCharacter) { @@ -1439,14 +1463,11 @@ TEST_F(AutofillManagerTest, GetCreditCardSuggestions_EmptyValue) { // Test that we sent the right values to the external delegate. external_delegate_->CheckSuggestions( - kDefaultPageID, Suggestion("Visa\xC2\xA0\xE2\x8B\xAF" - "3456", + kDefaultPageID, Suggestion("Visa" + kUTF8MidlineEllipsis + "3456", "04/99", kVisaCard, autofill_manager_->GetPackedCreditCardID(4)), - Suggestion("MasterCard\xC2\xA0\xE2\x8B\xAF" - "8765", - "10/98", kMasterCard, - autofill_manager_->GetPackedCreditCardID(5))); + Suggestion("MasterCard" + kUTF8MidlineEllipsis + "8765", "10/98", + kMasterCard, autofill_manager_->GetPackedCreditCardID(5))); } // Test that we return all credit card profile suggestions when the triggering @@ -1464,14 +1485,11 @@ TEST_F(AutofillManagerTest, GetCreditCardSuggestions_Whitespace) { // Test that we sent the right values to the external delegate. external_delegate_->CheckSuggestions( - kDefaultPageID, Suggestion("Visa\xC2\xA0\xE2\x8B\xAF" - "3456", + kDefaultPageID, Suggestion("Visa" + kUTF8MidlineEllipsis + "3456", "04/99", kVisaCard, autofill_manager_->GetPackedCreditCardID(4)), - Suggestion("MasterCard\xC2\xA0\xE2\x8B\xAF" - "8765", - "10/98", kMasterCard, - autofill_manager_->GetPackedCreditCardID(5))); + Suggestion("MasterCard" + kUTF8MidlineEllipsis + "8765", "10/98", + kMasterCard, autofill_manager_->GetPackedCreditCardID(5))); } // Test that we return all credit card profile suggestions when the triggering @@ -1489,14 +1507,11 @@ TEST_F(AutofillManagerTest, GetCreditCardSuggestions_StopCharsOnly) { // Test that we sent the right values to the external delegate. external_delegate_->CheckSuggestions( - kDefaultPageID, Suggestion("Visa\xC2\xA0\xE2\x8B\xAF" - "3456", + kDefaultPageID, Suggestion("Visa" + kUTF8MidlineEllipsis + "3456", "04/99", kVisaCard, autofill_manager_->GetPackedCreditCardID(4)), - Suggestion("MasterCard\xC2\xA0\xE2\x8B\xAF" - "8765", - "10/98", kMasterCard, - autofill_manager_->GetPackedCreditCardID(5))); + Suggestion("MasterCard" + kUTF8MidlineEllipsis + "8765", "10/98", + kMasterCard, autofill_manager_->GetPackedCreditCardID(5))); } // Test that we return all credit card profile suggestions when the triggering @@ -1523,8 +1538,7 @@ TEST_F(AutofillManagerTest, GetCreditCardSuggestions_StopCharsWithInput) { // Test that we sent the right value to the external delegate. external_delegate_->CheckSuggestions( - kDefaultPageID, Suggestion("MasterCard\xC2\xA0\xE2\x8B\xAF" - "3123", + kDefaultPageID, Suggestion("MasterCard" + kUTF8MidlineEllipsis + "3123", "08/17", kMasterCard, autofill_manager_->GetPackedCreditCardID(7))); } @@ -1544,8 +1558,7 @@ TEST_F(AutofillManagerTest, GetCreditCardSuggestions_MatchCharacter) { // Test that we sent the right values to the external delegate. external_delegate_->CheckSuggestions( - kDefaultPageID, Suggestion("Visa\xC2\xA0\xE2\x8B\xAF" - "3456", + kDefaultPageID, Suggestion("Visa" + kUTF8MidlineEllipsis + "3456", "04/99", kVisaCard, autofill_manager_->GetPackedCreditCardID(4))); } @@ -1563,15 +1576,13 @@ TEST_F(AutofillManagerTest, GetCreditCardSuggestions_NonCCNumber) { GetAutofillSuggestions(form, field); #if defined(OS_ANDROID) - static const char* kVisaSuggestion = - "Visa\xC2\xA0\xE2\x8B\xAF" - "3456"; - static const char* kMcSuggestion = - "MasterCard\xC2\xA0\xE2\x8B\xAF" - "8765"; + static const std::string kVisaSuggestion = + "Visa" + kUTF8MidlineEllipsis + "3456"; + static const std::string kMcSuggestion = + "MasterCard" + kUTF8MidlineEllipsis + "8765"; #else - static const char* kVisaSuggestion = "*3456"; - static const char* kMcSuggestion = "*8765"; + static const std::string kVisaSuggestion = "*3456"; + static const std::string kMcSuggestion = "*8765"; #endif // Test that we sent the right values to the external delegate. @@ -1629,16 +1640,26 @@ TEST_F(AutofillManagerTest, kDefaultPageID, Suggestion(l10n_util::GetStringUTF8( IDS_AUTOFILL_CREDIT_CARD_HTTP_WARNING_MESSAGE), - "", "", -1), + l10n_util::GetStringUTF8(IDS_AUTOFILL_HTTP_WARNING_LEARN_MORE), + "httpWarning", POPUP_ITEM_ID_HTTP_NOT_SECURE_WARNING_MESSAGE), +#if !defined(OS_ANDROID) + Suggestion("", "", "", POPUP_ITEM_ID_SEPARATOR), +#endif Suggestion( - l10n_util::GetStringUTF8(IDS_AUTOFILL_WARNING_INSECURE_CONNECTION), - "", "", -1)); + l10n_util::GetStringUTF8(IDS_AUTOFILL_WARNING_PAYMENT_DISABLED), "", + "", POPUP_ITEM_ID_INSECURE_CONTEXT_PAYMENT_DISABLED_MESSAGE)); - // Clear the test credit cards and try again -- we shouldn't return a warning. + // Clear the test credit cards and try again -- we should still show the + // warning. personal_data_.ClearCreditCards(); GetAutofillSuggestions(form, field); - // Autocomplete suggestions are queried, but not Autofill. - EXPECT_FALSE(external_delegate_->on_suggestions_returned_seen()); + // Test that we sent the right values to the external delegate. + external_delegate_->CheckSuggestions( + kDefaultPageID, + Suggestion(l10n_util::GetStringUTF8( + IDS_AUTOFILL_CREDIT_CARD_HTTP_WARNING_MESSAGE), + l10n_util::GetStringUTF8(IDS_AUTOFILL_HTTP_WARNING_LEARN_MORE), + "httpWarning", POPUP_ITEM_ID_HTTP_NOT_SECURE_WARNING_MESSAGE)); } // Test that we don't show the extra "Payment not secure" warning when the page @@ -1658,14 +1679,11 @@ TEST_F(AutofillManagerTest, // Test that we sent the right values to the external delegate. external_delegate_->CheckSuggestions( - kDefaultPageID, Suggestion("Visa\xC2\xA0\xE2\x8B\xAF" - "3456", + kDefaultPageID, Suggestion("Visa" + kUTF8MidlineEllipsis + "3456", "04/99", kVisaCard, autofill_manager_->GetPackedCreditCardID(4)), - Suggestion("MasterCard\xC2\xA0\xE2\x8B\xAF" - "8765", - "10/98", kMasterCard, - autofill_manager_->GetPackedCreditCardID(5))); + Suggestion("MasterCard" + kUTF8MidlineEllipsis + "8765", "10/98", + kMasterCard, autofill_manager_->GetPackedCreditCardID(5))); } // Test that we will eventually return the credit card signin promo when there @@ -1749,14 +1767,11 @@ TEST_F(AutofillManagerTest, // Test that we sent the right values to the external delegate. external_delegate_->CheckSuggestions( - kDefaultPageID, Suggestion("Visa\xC2\xA0\xE2\x8B\xAF" - "3456", + kDefaultPageID, Suggestion("Visa" + kUTF8MidlineEllipsis + "3456", "04/99", kVisaCard, autofill_manager_->GetPackedCreditCardID(4)), - Suggestion("MasterCard\xC2\xA0\xE2\x8B\xAF" - "8765", - "10/98", kMasterCard, - autofill_manager_->GetPackedCreditCardID(5))); + Suggestion("MasterCard" + kUTF8MidlineEllipsis + "8765", "10/98", + kMasterCard, autofill_manager_->GetPackedCreditCardID(5))); } // Test that we return credit card suggestions for secure pages that have a @@ -1776,14 +1791,11 @@ TEST_F(AutofillManagerTest, // Test that we sent the right values to the external delegate. external_delegate_->CheckSuggestions( - kDefaultPageID, Suggestion("Visa\xC2\xA0\xE2\x8B\xAF" - "3456", + kDefaultPageID, Suggestion("Visa" + kUTF8MidlineEllipsis + "3456", "04/99", kVisaCard, autofill_manager_->GetPackedCreditCardID(4)), - Suggestion("MasterCard\xC2\xA0\xE2\x8B\xAF" - "8765", - "10/98", kMasterCard, - autofill_manager_->GetPackedCreditCardID(5))); + Suggestion("MasterCard" + kUTF8MidlineEllipsis + "8765", "10/98", + kMasterCard, autofill_manager_->GetPackedCreditCardID(5))); } // Test that we return all credit card suggestions in the case that two cards @@ -1810,18 +1822,13 @@ TEST_F(AutofillManagerTest, GetCreditCardSuggestions_RepeatedObfuscatedNumber) { // Test that we sent the right values to the external delegate. external_delegate_->CheckSuggestions( - kDefaultPageID, Suggestion("Visa\xC2\xA0\xE2\x8B\xAF" - "3456", + kDefaultPageID, Suggestion("Visa" + kUTF8MidlineEllipsis + "3456", "04/99", kVisaCard, autofill_manager_->GetPackedCreditCardID(4)), - Suggestion("MasterCard\xC2\xA0\xE2\x8B\xAF" - "8765", - "10/98", kMasterCard, - autofill_manager_->GetPackedCreditCardID(5)), - Suggestion("MasterCard\xC2\xA0\xE2\x8B\xAF" - "3456", - "05/99", kMasterCard, - autofill_manager_->GetPackedCreditCardID(7))); + Suggestion("MasterCard" + kUTF8MidlineEllipsis + "8765", "10/98", + kMasterCard, autofill_manager_->GetPackedCreditCardID(5)), + Suggestion("MasterCard" + kUTF8MidlineEllipsis + "3456", "05/99", + kMasterCard, autofill_manager_->GetPackedCreditCardID(7))); } // Test that we return profile and credit card suggestions for combined forms. @@ -1847,14 +1854,11 @@ TEST_F(AutofillManagerTest, GetAddressAndCreditCardSuggestions) { // Test that we sent the credit card suggestions to the external delegate. external_delegate_->CheckSuggestions( - kPageID2, Suggestion("Visa\xC2\xA0\xE2\x8B\xAF" - "3456", + kPageID2, Suggestion("Visa" + kUTF8MidlineEllipsis + "3456", "04/99", kVisaCard, autofill_manager_->GetPackedCreditCardID(4)), - Suggestion("MasterCard\xC2\xA0\xE2\x8B\xAF" - "8765", - "10/98", kMasterCard, - autofill_manager_->GetPackedCreditCardID(5))); + Suggestion("MasterCard" + kUTF8MidlineEllipsis + "8765", "10/98", + kMasterCard, autofill_manager_->GetPackedCreditCardID(5))); } // Test that for non-https forms with both address and credit card fields, we @@ -3667,17 +3671,38 @@ TEST_F(AutofillManagerTest, FormSubmittedWithDefaultValues) { } // Tests that credit card data are saved for forms on https -TEST_F(AutofillManagerTest, ImportFormDataCreditCardHTTPS) { +// TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot. +#if defined(OS_ANDROID) +#define MAYBE_ImportFormDataCreditCardHTTPS \ + DISABLED_ImportFormDataCreditCardHTTPS +#else +#define MAYBE_ImportFormDataCreditCardHTTPS ImportFormDataCreditCardHTTPS +#endif +TEST_F(AutofillManagerTest, MAYBE_ImportFormDataCreditCardHTTPS) { TestSaveCreditCards(true); } // Tests that credit card data are saved for forms on http -TEST_F(AutofillManagerTest, ImportFormDataCreditCardHTTP) { +// TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot. +#if defined(OS_ANDROID) +#define MAYBE_ImportFormDataCreditCardHTTP DISABLED_ImportFormDataCreditCardHTTP +#else +#define MAYBE_ImportFormDataCreditCardHTTP ImportFormDataCreditCardHTTP +#endif +TEST_F(AutofillManagerTest, MAYBE_ImportFormDataCreditCardHTTP) { TestSaveCreditCards(false); } // Tests that credit card data are saved when autocomplete=off for CC field. -TEST_F(AutofillManagerTest, CreditCardSavedWhenAutocompleteOff) { +// TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot. +#if defined(OS_ANDROID) +#define MAYBE_CreditCardSavedWhenAutocompleteOff \ + DISABLED_CreditCardSavedWhenAutocompleteOff +#else +#define MAYBE_CreditCardSavedWhenAutocompleteOff \ + CreditCardSavedWhenAutocompleteOff +#endif +TEST_F(AutofillManagerTest, MAYBE_CreditCardSavedWhenAutocompleteOff) { // Set up our form data. FormData form; CreateTestCreditCardFormData(&form, false, false); @@ -4376,8 +4401,7 @@ TEST_F(AutofillManagerTest, GetAutofillSuggestions(form, number_field); external_delegate_->CheckSuggestions( - kDefaultPageID, Suggestion("Visa\xC2\xA0\xE2\x8B\xAF" - "3456", + kDefaultPageID, Suggestion("Visa" + kUTF8MidlineEllipsis + "3456", "04/99", kVisaCard, autofill_manager_->GetPackedCreditCardID(4))); } @@ -4466,7 +4490,13 @@ TEST_F(AutofillManagerTest, FillInUpdatedExpirationDate) { "4012888888881881"); } -TEST_F(AutofillManagerTest, UploadCreditCard) { +// TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot. +#if defined(OS_ANDROID) +#define MAYBE_UploadCreditCard DISABLED_UploadCreditCard +#else +#define MAYBE_UploadCreditCard UploadCreditCard +#endif +TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard) { personal_data_.ClearAutofillProfiles(); autofill_manager_->set_credit_card_upload_enabled(true); @@ -4500,7 +4530,13 @@ TEST_F(AutofillManagerTest, UploadCreditCard) { AutofillMetrics::UPLOAD_OFFERED, 1); } -TEST_F(AutofillManagerTest, UploadCreditCard_FeatureNotEnabled) { +// TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot. +#if defined(OS_ANDROID) +#define MAYBE_UploadCreditCard_FeatureNotEnabled DISABLED_UploadCreditCard_FeatureNotEnabled +#else +#define MAYBE_UploadCreditCard_FeatureNotEnabled UploadCreditCard_FeatureNotEnabled +#endif +TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_FeatureNotEnabled) { personal_data_.ClearAutofillProfiles(); autofill_manager_->set_credit_card_upload_enabled(false); @@ -4535,7 +4571,13 @@ TEST_F(AutofillManagerTest, UploadCreditCard_FeatureNotEnabled) { histogram_tester.ExpectTotalCount("Autofill.CardUploadDecisionExpanded", 0); } -TEST_F(AutofillManagerTest, UploadCreditCard_CvcUnavailable) { +// TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot. +#if defined(OS_ANDROID) +#define MAYBE_UploadCreditCard_CvcUnavailable DISABLED_UploadCreditCard_CvcUnavailable +#else +#define MAYBE_UploadCreditCard_CvcUnavailable UploadCreditCard_CvcUnavailable +#endif +TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_CvcUnavailable) { personal_data_.ClearAutofillProfiles(); autofill_manager_->set_credit_card_upload_enabled(true); @@ -4571,7 +4613,60 @@ TEST_F(AutofillManagerTest, UploadCreditCard_CvcUnavailable) { "Autofill.CardUploadDecisionExpanded", AutofillMetrics::UPLOAD_NOT_OFFERED_NO_CVC, 1); - rappor::TestRapporService* rappor_service = + rappor::TestRapporServiceImpl* rappor_service = + autofill_client_.test_rappor_service(); + EXPECT_EQ(1, rappor_service->GetReportsCount()); + std::string sample; + rappor::RapporType type; + EXPECT_TRUE(rappor_service->GetRecordedSampleForMetric( + "Autofill.CardUploadNotOfferedNoCvc", &sample, &type)); + EXPECT_EQ("myform.com", sample); + EXPECT_EQ(rappor::ETLD_PLUS_ONE_RAPPOR_TYPE, type); +} + +// TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot. +#if defined(OS_ANDROID) +#define MAYBE_UploadCreditCard_CvcInvalidLength DISABLED_UploadCreditCard_CvcInvalidLength +#else +#define MAYBE_UploadCreditCard_CvcInvalidLength UploadCreditCard_CvcInvalidLength +#endif +TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_CvcInvalidLength) { + personal_data_.ClearAutofillProfiles(); + autofill_manager_->set_credit_card_upload_enabled(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, true, false); + FormsSeen(std::vector<FormData>(1, credit_card_form)); + + // 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("11"); + credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + credit_card_form.fields[4].value = ASCIIToUTF16("1234"); + + base::HistogramTester histogram_tester; + + // Neither a local save nor an upload should happen in this case. + EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0); + FormSubmitted(credit_card_form); + EXPECT_FALSE(autofill_manager_->credit_card_was_uploaded()); + + // Verify that the correct histogram entry (and only that) was logged. + histogram_tester.ExpectUniqueSample( + "Autofill.CardUploadDecisionExpanded", + AutofillMetrics::UPLOAD_NOT_OFFERED_NO_CVC, 1); + + rappor::TestRapporServiceImpl* rappor_service = autofill_client_.test_rappor_service(); EXPECT_EQ(1, rappor_service->GetReportsCount()); std::string sample; @@ -4582,7 +4677,13 @@ TEST_F(AutofillManagerTest, UploadCreditCard_CvcUnavailable) { EXPECT_EQ(rappor::ETLD_PLUS_ONE_RAPPOR_TYPE, type); } -TEST_F(AutofillManagerTest, UploadCreditCard_MultipleCvcFields) { +// TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot. +#if defined(OS_ANDROID) +#define MAYBE_UploadCreditCard_MultipleCvcFields DISABLED_UploadCreditCard_MultipleCvcFields +#else +#define MAYBE_UploadCreditCard_MultipleCvcFields UploadCreditCard_MultipleCvcFields +#endif +TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_MultipleCvcFields) { autofill_manager_->set_credit_card_upload_enabled(true); // Remove the profiles that were created in the TestPersonalDataManager @@ -4641,7 +4742,13 @@ TEST_F(AutofillManagerTest, UploadCreditCard_MultipleCvcFields) { AutofillMetrics::UPLOAD_OFFERED, 1); } -TEST_F(AutofillManagerTest, UploadCreditCard_NoProfileAvailable) { +// TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot. +#if defined(OS_ANDROID) +#define MAYBE_UploadCreditCard_NoProfileAvailable DISABLED_UploadCreditCard_NoProfileAvailable +#else +#define MAYBE_UploadCreditCard_NoProfileAvailable UploadCreditCard_NoProfileAvailable +#endif +TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_NoProfileAvailable) { personal_data_.ClearAutofillProfiles(); autofill_manager_->set_credit_card_upload_enabled(true); @@ -4671,7 +4778,7 @@ TEST_F(AutofillManagerTest, UploadCreditCard_NoProfileAvailable) { "Autofill.CardUploadDecisionExpanded", AutofillMetrics::UPLOAD_NOT_OFFERED_NO_ADDRESS, 1); - rappor::TestRapporService* rappor_service = + rappor::TestRapporServiceImpl* rappor_service = autofill_client_.test_rappor_service(); EXPECT_EQ(1, rappor_service->GetReportsCount()); std::string sample; @@ -4682,7 +4789,13 @@ TEST_F(AutofillManagerTest, UploadCreditCard_NoProfileAvailable) { EXPECT_EQ(rappor::ETLD_PLUS_ONE_RAPPOR_TYPE, type); } -TEST_F(AutofillManagerTest, UploadCreditCard_NoNameAvailable) { +// TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot. +#if defined(OS_ANDROID) +#define MAYBE_UploadCreditCard_NoNameAvailable DISABLED_UploadCreditCard_NoNameAvailable +#else +#define MAYBE_UploadCreditCard_NoNameAvailable UploadCreditCard_NoNameAvailable +#endif +TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_NoNameAvailable) { personal_data_.ClearAutofillProfiles(); autofill_manager_->set_credit_card_upload_enabled(true); @@ -4718,7 +4831,7 @@ TEST_F(AutofillManagerTest, UploadCreditCard_NoNameAvailable) { "Autofill.CardUploadDecisionExpanded", AutofillMetrics::UPLOAD_NOT_OFFERED_NO_NAME, 1); - rappor::TestRapporService* rappor_service = + rappor::TestRapporServiceImpl* rappor_service = autofill_client_.test_rappor_service(); EXPECT_EQ(1, rappor_service->GetReportsCount()); std::string sample; @@ -4729,7 +4842,13 @@ TEST_F(AutofillManagerTest, UploadCreditCard_NoNameAvailable) { EXPECT_EQ(rappor::ETLD_PLUS_ONE_RAPPOR_TYPE, type); } -TEST_F(AutofillManagerTest, UploadCreditCard_ZipCodesConflict) { +// TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot. +#if defined(OS_ANDROID) +#define MAYBE_UploadCreditCard_ZipCodesConflict DISABLED_UploadCreditCard_ZipCodesConflict +#else +#define MAYBE_UploadCreditCard_ZipCodesConflict UploadCreditCard_ZipCodesConflict +#endif +TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_ZipCodesConflict) { personal_data_.ClearAutofillProfiles(); autofill_manager_->set_credit_card_upload_enabled(true); @@ -4774,7 +4893,13 @@ TEST_F(AutofillManagerTest, UploadCreditCard_ZipCodesConflict) { AutofillMetrics::UPLOAD_NOT_OFFERED_CONFLICTING_ZIPS, 1); } -TEST_F(AutofillManagerTest, UploadCreditCard_ZipCodesHavePrefixMatch) { +// TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot. +#if defined(OS_ANDROID) +#define MAYBE_UploadCreditCard_ZipCodesHavePrefixMatch DISABLED_UploadCreditCard_ZipCodesHavePrefixMatch +#else +#define MAYBE_UploadCreditCard_ZipCodesHavePrefixMatch UploadCreditCard_ZipCodesHavePrefixMatch +#endif +TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_ZipCodesHavePrefixMatch) { personal_data_.ClearAutofillProfiles(); autofill_manager_->set_credit_card_upload_enabled(true); @@ -4819,7 +4944,13 @@ TEST_F(AutofillManagerTest, UploadCreditCard_ZipCodesHavePrefixMatch) { AutofillMetrics::UPLOAD_OFFERED, 1); } -TEST_F(AutofillManagerTest, UploadCreditCard_NoZipCodeAvailable) { +// TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot. +#if defined(OS_ANDROID) +#define MAYBE_UploadCreditCard_NoZipCodeAvailable DISABLED_UploadCreditCard_NoZipCodeAvailable +#else +#define MAYBE_UploadCreditCard_NoZipCodeAvailable UploadCreditCard_NoZipCodeAvailable +#endif +TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_NoZipCodeAvailable) { personal_data_.ClearAutofillProfiles(); autofill_manager_->set_credit_card_upload_enabled(true); @@ -4863,7 +4994,13 @@ TEST_F(AutofillManagerTest, UploadCreditCard_NoZipCodeAvailable) { AutofillMetrics::UPLOAD_NOT_OFFERED_NO_ZIP_CODE, 1); } -TEST_F(AutofillManagerTest, UploadCreditCard_NamesMatchLoosely) { +// TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot. +#if defined(OS_ANDROID) +#define MAYBE_UploadCreditCard_NamesMatchLoosely DISABLED_UploadCreditCard_NamesMatchLoosely +#else +#define MAYBE_UploadCreditCard_NamesMatchLoosely UploadCreditCard_NamesMatchLoosely +#endif +TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_NamesMatchLoosely) { personal_data_.ClearAutofillProfiles(); autofill_manager_->set_credit_card_upload_enabled(true); @@ -4911,7 +5048,13 @@ TEST_F(AutofillManagerTest, UploadCreditCard_NamesMatchLoosely) { AutofillMetrics::UPLOAD_OFFERED, 1); } -TEST_F(AutofillManagerTest, UploadCreditCard_NamesHaveToMatch) { +// TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot. +#if defined(OS_ANDROID) +#define MAYBE_UploadCreditCard_NamesHaveToMatch DISABLED_UploadCreditCard_NamesHaveToMatch +#else +#define MAYBE_UploadCreditCard_NamesHaveToMatch UploadCreditCard_NamesHaveToMatch +#endif +TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_NamesHaveToMatch) { personal_data_.ClearAutofillProfiles(); autofill_manager_->set_credit_card_upload_enabled(true); @@ -4955,7 +5098,7 @@ TEST_F(AutofillManagerTest, UploadCreditCard_NamesHaveToMatch) { "Autofill.CardUploadDecisionExpanded", AutofillMetrics::UPLOAD_NOT_OFFERED_CONFLICTING_NAMES, 1); - rappor::TestRapporService* rappor_service = + rappor::TestRapporServiceImpl* rappor_service = autofill_client_.test_rappor_service(); EXPECT_EQ(1, rappor_service->GetReportsCount()); std::string sample; @@ -4966,7 +5109,13 @@ TEST_F(AutofillManagerTest, UploadCreditCard_NamesHaveToMatch) { EXPECT_EQ(rappor::ETLD_PLUS_ONE_RAPPOR_TYPE, type); } -TEST_F(AutofillManagerTest, UploadCreditCard_UploadDetailsFails) { +// TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot. +#if defined(OS_ANDROID) +#define MAYBE_UploadCreditCard_UploadDetailsFails DISABLED_UploadCreditCard_UploadDetailsFails +#else +#define MAYBE_UploadCreditCard_UploadDetailsFails UploadCreditCard_UploadDetailsFails +#endif +TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_UploadDetailsFails) { personal_data_.ClearAutofillProfiles(); autofill_manager_->set_credit_card_upload_enabled(true); @@ -5089,11 +5238,10 @@ TEST_F(AutofillManagerTest, DisplayCreditCardSuggestionsWithMatchingTokens) { GetAutofillSuggestions(form, field); #if defined(OS_ANDROID) - static const char* kVisaSuggestion = - "Visa\xC2\xA0\xE2\x8B\xAF" - "3456"; + static const std::string kVisaSuggestion = + "Visa" + kUTF8MidlineEllipsis + "3456"; #else - static const char* kVisaSuggestion = "*3456"; + static const std::string kVisaSuggestion = "*3456"; #endif external_delegate_->CheckSuggestions( diff --git a/chromium/components/autofill/core/browser/autofill_merge_unittest.cc b/chromium/components/autofill/core/browser/autofill_merge_unittest.cc index f38ffaf95f2..a4aceaab132 100644 --- a/chromium/components/autofill/core/browser/autofill_merge_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_merge_unittest.cc @@ -8,7 +8,6 @@ #include <memory> #include <vector> -#include "base/feature_list.h" #include "base/files/file_enumerator.h" #include "base/files/file_path.h" #include "base/macros.h" @@ -17,7 +16,6 @@ #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" -#include "base/test/scoped_feature_list.h" #include "components/autofill/core/browser/autofill_experiments.h" #include "components/autofill/core/browser/autofill_test_utils.h" #include "components/autofill/core/browser/autofill_type.h" @@ -186,7 +184,6 @@ class AutofillMergeTest : public DataDrivenTest, PersonalDataManagerMock personal_data_; private: - base::test::ScopedFeatureList scoped_feature_list_; std::map<std::string, ServerFieldType> string_to_field_type_map_; DISALLOW_COPY_AND_ASSIGN(AutofillMergeTest); @@ -205,7 +202,6 @@ AutofillMergeTest::~AutofillMergeTest() { void AutofillMergeTest::SetUp() { test::DisableSystemServices(nullptr); - scoped_feature_list_.InitAndEnableFeature(kAutofillProfileCleanup); } void AutofillMergeTest::TearDown() { diff --git a/chromium/components/autofill/core/browser/autofill_metrics.cc b/chromium/components/autofill/core/browser/autofill_metrics.cc index e655f7fe66f..66a7bfcc967 100644 --- a/chromium/components/autofill/core/browser/autofill_metrics.cc +++ b/chromium/components/autofill/core/browser/autofill_metrics.cc @@ -675,10 +675,17 @@ void AutofillMetrics::LogIsQueriedCreditCardFormSecure(bool is_secure) { UMA_HISTOGRAM_BOOLEAN("Autofill.QueriedCreditCardFormIsSecure", is_secure); } +// static +void AutofillMetrics::LogShowedHttpNotSecureExplanation() { + base::RecordAction( + base::UserMetricsAction("Autofill_ShowedHttpNotSecureExplanation")); +} + AutofillMetrics::FormEventLogger::FormEventLogger(bool is_for_credit_card) : is_for_credit_card_(is_for_credit_card), is_server_data_available_(false), is_local_data_available_(false), + is_context_secure_(false), has_logged_interacted_(false), has_logged_suggestions_shown_(false), has_logged_masked_server_card_suggestion_selected_(false), @@ -686,8 +693,7 @@ AutofillMetrics::FormEventLogger::FormEventLogger(bool is_for_credit_card) has_logged_will_submit_(false), has_logged_submitted_(false), logged_suggestion_filled_was_server_data_(false), - logged_suggestion_filled_was_masked_server_card_(false) { -} + logged_suggestion_filled_was_masked_server_card_(false) {} void AutofillMetrics::FormEventLogger::OnDidInteractWithAutofillableForm() { if (!has_logged_interacted_) { @@ -814,6 +820,10 @@ void AutofillMetrics::FormEventLogger::OnWillSubmitForm() { Log(AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE); } + if (has_logged_suggestions_shown_) { + Log(AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE); + } + base::RecordAction(base::UserMetricsAction("Autofill_OnWillSubmitForm")); } @@ -837,6 +847,10 @@ void AutofillMetrics::FormEventLogger::OnFormSubmitted() { } else { Log(AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE); } + + if (has_logged_suggestions_shown_) { + Log(AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE); + } } void AutofillMetrics::FormEventLogger::Log(FormEvent event) const { @@ -848,6 +862,14 @@ void AutofillMetrics::FormEventLogger::Log(FormEvent event) const { name += "Address"; LogUMAHistogramEnumeration(name, event, NUM_FORM_EVENTS); + // Log again in a different histogram for credit card forms on nonsecure + // pages, so that form interactions on nonsecure pages can be analyzed on + // their own. + if (is_for_credit_card_ && !is_context_secure_) { + LogUMAHistogramEnumeration(name + ".OnNonsecurePage", event, + NUM_FORM_EVENTS); + } + // Logging again in a different histogram for segmentation purposes. // TODO(waltercacau): Re-evaluate if we still need such fine grained // segmentation. http://crbug.com/454018 diff --git a/chromium/components/autofill/core/browser/autofill_metrics.h b/chromium/components/autofill/core/browser/autofill_metrics.h index b8e569baf69..6fe8a34ebc3 100644 --- a/chromium/components/autofill/core/browser/autofill_metrics.h +++ b/chromium/components/autofill/core/browser/autofill_metrics.h @@ -383,6 +383,13 @@ class AutofillMetrics { FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, + // A dropdown with suggestions was shown and a form was submitted after + // that. + FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, + // A dropdown with suggestions was shown and a form is about to be + // submitted. If the submission is not interrupted by JavaScript, the "form + // submitted" event above will also be logged. + FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, NUM_FORM_EVENTS, }; @@ -656,6 +663,10 @@ class AutofillMetrics { // context. static void LogIsQueriedCreditCardFormSecure(bool is_secure); + // This should be called when the user selects the Form-Not-Secure warning + // suggestion to show an explanation of the warning. + static void LogShowedHttpNotSecureExplanation(); + // Utility to autofill form events in the relevant histograms depending on // the presence of server and/or local data. class FormEventLogger { @@ -670,6 +681,10 @@ class AutofillMetrics { is_local_data_available_ = is_local_data_available; } + inline void set_is_context_secure(bool is_context_secure) { + is_context_secure_ = is_context_secure; + } + void OnDidInteractWithAutofillableForm(); void OnDidPollSuggestions(const FormFieldData& field); @@ -694,6 +709,7 @@ class AutofillMetrics { bool is_for_credit_card_; bool is_server_data_available_; bool is_local_data_available_; + bool is_context_secure_; bool has_logged_interacted_; bool has_logged_suggestions_shown_; bool has_logged_masked_server_card_suggestion_selected_; diff --git a/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc b/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc index 681b131a408..f9799f5e8af 100644 --- a/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc @@ -22,6 +22,7 @@ #include "components/autofill/core/browser/autofill_test_utils.h" #include "components/autofill/core/browser/payments/payments_client.h" #include "components/autofill/core/browser/personal_data_manager.h" +#include "components/autofill/core/browser/popup_item_ids.h" #include "components/autofill/core/browser/test_autofill_client.h" #include "components/autofill/core/browser/test_autofill_driver.h" #include "components/autofill/core/browser/webdata/autofill_webdata_service.h" @@ -42,7 +43,7 @@ using base::ASCIIToUTF16; using base::Bucket; using base::TimeTicks; -using rappor::TestRapporService; +using rappor::TestRapporServiceImpl; using ::testing::ElementsAre; namespace autofill { @@ -1885,6 +1886,7 @@ TEST_F(AutofillMetricsTest, QueriedCreditCardFormIsSecure) { form.name = ASCIIToUTF16("TestForm"); form.origin = GURL("http://example.com/form.html"); form.action = GURL("http://example.com/submit.html"); + autofill_client_.set_form_origin(form.origin); FormFieldData field; std::vector<ServerFieldType> field_types; @@ -1918,6 +1920,7 @@ TEST_F(AutofillMetricsTest, QueriedCreditCardFormIsSecure) { autofill_manager_->Reset(); form.origin = GURL("https://example.com/form.html"); form.action = GURL("https://example.com/submit.html"); + autofill_client_.set_form_origin(form.origin); autofill_manager_->AddSeenForm(form, field_types, field_types); // Simulate an Autofill query on a credit card field (HTTPS form). @@ -2413,6 +2416,24 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) { autofill_manager_->AddSeenForm(form, field_types, field_types); { + // Simulating submission with suggestion shown. + base::HistogramTester histogram_tester; + autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field); + autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF()); + autofill_manager_->SubmitForm(form, TimeTicks::Now()); + histogram_tester.ExpectBucketCount( + "Autofill.FormEvents.CreditCard", + AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 1); + histogram_tester.ExpectBucketCount( + "Autofill.FormEvents.CreditCard", + AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 1); + } + + // Reset the autofill manager state. + autofill_manager_->Reset(); + autofill_manager_->AddSeenForm(form, field_types, field_types); + + { // Simulating submission with filled local data. base::HistogramTester histogram_tester; autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF()); @@ -2496,6 +2517,9 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) { AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", + AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0); + histogram_tester.ExpectBucketCount( + "Autofill.FormEvents.CreditCard", AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", @@ -2510,6 +2534,9 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) { AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", + AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0); + histogram_tester.ExpectBucketCount( + "Autofill.FormEvents.CreditCard", AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", @@ -2526,27 +2553,45 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) { autofill_manager_->AddSeenForm(form, field_types, field_types); { - // Simulating submission without previous interaction. + // Simulating submission with suggestion shown but without previous + // interaction. base::HistogramTester histogram_tester; + autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field); autofill_manager_->SubmitForm(form, TimeTicks::Now()); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", - AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 0); + AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", - AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0); + AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", - AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0); + AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", - AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 0); + AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", - AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0); + AutofillMetrics:: + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, + 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", - AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0); + AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0); + histogram_tester.ExpectBucketCount( + "Autofill.FormEvents.CreditCard", + AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 0); + histogram_tester.ExpectBucketCount( + "Autofill.FormEvents.CreditCard", + AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 0); + histogram_tester.ExpectBucketCount( + "Autofill.FormEvents.CreditCard", + AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0); + histogram_tester.ExpectBucketCount( + "Autofill.FormEvents.CreditCard", + AutofillMetrics:: + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, + 0); } } @@ -2600,6 +2645,24 @@ TEST_F(AutofillMetricsTest, CreditCardWillSubmitFormEvents) { autofill_manager_->AddSeenForm(form, field_types, field_types); { + // Simulating submission with suggestion shown. + base::HistogramTester histogram_tester; + autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field); + autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF()); + autofill_manager_->WillSubmitForm(form, TimeTicks::Now()); + histogram_tester.ExpectBucketCount( + "Autofill.FormEvents.CreditCard", + AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 1); + histogram_tester.ExpectBucketCount( + "Autofill.FormEvents.CreditCard", + AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0); + } + + // Reset the autofill manager state. + autofill_manager_->Reset(); + autofill_manager_->AddSeenForm(form, field_types, field_types); + + { // Simulating submission with filled local data. base::HistogramTester histogram_tester; autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF()); @@ -2683,6 +2746,9 @@ TEST_F(AutofillMetricsTest, CreditCardWillSubmitFormEvents) { AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", + AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0); + histogram_tester.ExpectBucketCount( + "Autofill.FormEvents.CreditCard", AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", @@ -2697,6 +2763,9 @@ TEST_F(AutofillMetricsTest, CreditCardWillSubmitFormEvents) { AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", + AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0); + histogram_tester.ExpectBucketCount( + "Autofill.FormEvents.CreditCard", AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", @@ -2713,27 +2782,45 @@ TEST_F(AutofillMetricsTest, CreditCardWillSubmitFormEvents) { autofill_manager_->AddSeenForm(form, field_types, field_types); { - // Simulating submission without previous interaction. + // Simulating submission with suggestion shown but without previous + // interaction. base::HistogramTester histogram_tester; + autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field); autofill_manager_->WillSubmitForm(form, TimeTicks::Now()); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", - AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 0); + AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", - AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0); + AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", - AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0); + AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", - AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 0); + AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", - AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0); + AutofillMetrics:: + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, + 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", - AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0); + AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0); + histogram_tester.ExpectBucketCount( + "Autofill.FormEvents.CreditCard", + AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 0); + histogram_tester.ExpectBucketCount( + "Autofill.FormEvents.CreditCard", + AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 0); + histogram_tester.ExpectBucketCount( + "Autofill.FormEvents.CreditCard", + AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0); + histogram_tester.ExpectBucketCount( + "Autofill.FormEvents.CreditCard", + AutofillMetrics:: + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, + 0); } } @@ -2991,6 +3078,24 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) { autofill_manager_->AddSeenForm(form, field_types, field_types); { + // Simulating submission with suggestion shown. + base::HistogramTester histogram_tester; + autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field); + autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF()); + autofill_manager_->SubmitForm(form, TimeTicks::Now()); + histogram_tester.ExpectBucketCount( + "Autofill.FormEvents.Address", + AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 1); + histogram_tester.ExpectBucketCount( + "Autofill.FormEvents.Address", + AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 1); + } + + // Reset the autofill manager state. + autofill_manager_->Reset(); + autofill_manager_->AddSeenForm(form, field_types, field_types); + + { // Simulating submission with filled local data. base::HistogramTester histogram_tester; autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF()); @@ -3043,6 +3148,9 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) { AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", + AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0); + histogram_tester.ExpectBucketCount( + "Autofill.FormEvents.Address", AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", @@ -3052,6 +3160,9 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) { AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", + AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0); + histogram_tester.ExpectBucketCount( + "Autofill.FormEvents.Address", AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", @@ -3063,11 +3174,16 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) { autofill_manager_->AddSeenForm(form, field_types, field_types); { - // Simulating submission without previous interaction. + // Simulating submission with suggestion show but without previous + // interaction. base::HistogramTester histogram_tester; + autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field); autofill_manager_->SubmitForm(form, TimeTicks::Now()); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", + AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0); + histogram_tester.ExpectBucketCount( + "Autofill.FormEvents.Address", AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", @@ -3077,6 +3193,14 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) { AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", + AutofillMetrics:: + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, + 0); + histogram_tester.ExpectBucketCount( + "Autofill.FormEvents.Address", + AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0); + histogram_tester.ExpectBucketCount( + "Autofill.FormEvents.Address", AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", @@ -3084,6 +3208,11 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) { histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0); + histogram_tester.ExpectBucketCount( + "Autofill.FormEvents.Address", + AutofillMetrics:: + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, + 0); } } @@ -3135,6 +3264,24 @@ TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) { autofill_manager_->AddSeenForm(form, field_types, field_types); { + // Simulating submission with suggestion shown. + base::HistogramTester histogram_tester; + autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field); + autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF()); + autofill_manager_->WillSubmitForm(form, TimeTicks::Now()); + histogram_tester.ExpectBucketCount( + "Autofill.FormEvents.Address", + AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 1); + histogram_tester.ExpectBucketCount( + "Autofill.FormEvents.Address", + AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0); + } + + // Reset the autofill manager state. + autofill_manager_->Reset(); + autofill_manager_->AddSeenForm(form, field_types, field_types); + + { // Simulating submission with filled local data. base::HistogramTester histogram_tester; autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF()); @@ -3184,9 +3331,26 @@ TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) { autofill_manager_->WillSubmitForm(form, TimeTicks::Now()); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", + AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0); + histogram_tester.ExpectBucketCount( + "Autofill.FormEvents.Address", AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", + AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 0); + histogram_tester.ExpectBucketCount( + "Autofill.FormEvents.Address", + AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0); + histogram_tester.ExpectBucketCount( + "Autofill.FormEvents.Address", + AutofillMetrics:: + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, + 0); + histogram_tester.ExpectBucketCount( + "Autofill.FormEvents.Address", + AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0); + histogram_tester.ExpectBucketCount( + "Autofill.FormEvents.Address", AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", @@ -3194,6 +3358,11 @@ TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) { histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0); + histogram_tester.ExpectBucketCount( + "Autofill.FormEvents.Address", + AutofillMetrics:: + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, + 0); } // Reset the autofill manager state. @@ -3201,11 +3370,16 @@ TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) { autofill_manager_->AddSeenForm(form, field_types, field_types); { - // Simulating submission without previous interaction. + // Simulating submission with suggestion shown but without previous + // interaction. base::HistogramTester histogram_tester; + autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field); autofill_manager_->WillSubmitForm(form, TimeTicks::Now()); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", + AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0); + histogram_tester.ExpectBucketCount( + "Autofill.FormEvents.Address", AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", @@ -3215,6 +3389,14 @@ TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) { AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", + AutofillMetrics:: + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, + 0); + histogram_tester.ExpectBucketCount( + "Autofill.FormEvents.Address", + AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0); + histogram_tester.ExpectBucketCount( + "Autofill.FormEvents.Address", AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 0); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", @@ -3222,6 +3404,11 @@ TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) { histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0); + histogram_tester.ExpectBucketCount( + "Autofill.FormEvents.Address", + AutofillMetrics:: + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, + 0); } } @@ -4035,7 +4222,7 @@ class AutofillMetricsParseQueryResponseTest : public testing::Test { } protected: - TestRapporService rappor_service_; + TestRapporServiceImpl rappor_service_; std::vector<std::unique_ptr<FormStructure>> owned_forms_; std::vector<FormStructure*> forms_; }; @@ -4139,4 +4326,125 @@ TEST_F(AutofillMetricsParseQueryResponseTest, PartialNoServerData) { EXPECT_EQ(0, rappor_service_.GetReportsCount()); } +// Test that the Form-Not-Secure warning user action is recorded. +TEST_F(AutofillMetricsTest, ShowHttpNotSecureExplanationUserAction) { + base::UserActionTester user_action_tester; + external_delegate_->DidAcceptSuggestion( + ASCIIToUTF16("Test"), POPUP_ITEM_ID_HTTP_NOT_SECURE_WARNING_MESSAGE, 0); + EXPECT_EQ(1, user_action_tester.GetActionCount( + "Autofill_ShowedHttpNotSecureExplanation")); +} + +// Tests that credit card form submissions are logged specially when the form is +// on a non-secure page. +TEST_F(AutofillMetricsTest, NonsecureCreditCardForm) { + personal_data_->RecreateCreditCards( + true /* include_local_credit_card */, + false /* include_masked_server_credit_card */, + false /* include_full_server_credit_card */); + + // Set up our form data. + FormData form; + form.name = ASCIIToUTF16("TestForm"); + form.origin = GURL("http://example.com/form.html"); + form.action = GURL("http://example.com/submit.html"); + autofill_client_.set_form_origin(form.origin); + + FormFieldData field; + std::vector<ServerFieldType> field_types; + test::CreateTestFormField("Name on card", "cc-name", "", "text", &field); + form.fields.push_back(field); + field_types.push_back(CREDIT_CARD_NAME_FULL); + test::CreateTestFormField("Credit card", "card", "", "text", &field); + form.fields.push_back(field); + field_types.push_back(CREDIT_CARD_NUMBER); + test::CreateTestFormField("Month", "card_month", "", "text", &field); + form.fields.push_back(field); + field_types.push_back(CREDIT_CARD_EXP_MONTH); + + // Simulate having seen this form on page load. + // |form_structure| will be owned by |autofill_manager_|. + autofill_manager_->AddSeenForm(form, field_types, field_types); + + // Simulate an Autofill query on a credit card field. + { + base::UserActionTester user_action_tester; + autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF()); + EXPECT_EQ(1, user_action_tester.GetActionCount( + "Autofill_PolledCreditCardSuggestions")); + } + + // Simulate submitting the credit card form. + { + base::HistogramTester histograms; + autofill_manager_->SubmitForm(form, TimeTicks::Now()); + histograms.ExpectBucketCount( + "Autofill.FormEvents.CreditCard.OnNonsecurePage", + AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1); + histograms.ExpectBucketCount( + "Autofill.FormEvents.CreditCard", + AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1); + histograms.ExpectBucketCount( + "Autofill.FormEvents.CreditCard.WithOnlyLocalData", + AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1); + } +} + +// Tests that credit card form submissions are *not* logged specially when the +// form is *not* on a non-secure page. +TEST_F(AutofillMetricsTest, + NonsecureCreditCardFormMetricsNotRecordedOnSecurePage) { + personal_data_->RecreateCreditCards( + true /* include_local_credit_card */, + false /* include_masked_server_credit_card */, + false /* include_full_server_credit_card */); + + // Set up our form data. + FormData form; + form.name = ASCIIToUTF16("TestForm"); + form.origin = GURL("https://example.com/form.html"); + form.action = GURL("http://example.com/submit.html"); + + FormFieldData field; + std::vector<ServerFieldType> field_types; + test::CreateTestFormField("Name on card", "cc-name", "", "text", &field); + form.fields.push_back(field); + field_types.push_back(CREDIT_CARD_NAME_FULL); + test::CreateTestFormField("Credit card", "card", "", "text", &field); + form.fields.push_back(field); + field_types.push_back(CREDIT_CARD_NUMBER); + test::CreateTestFormField("Month", "card_month", "", "text", &field); + form.fields.push_back(field); + field_types.push_back(CREDIT_CARD_EXP_MONTH); + + // Simulate having seen this form on page load. + // |form_structure| will be owned by |autofill_manager_|. + autofill_manager_->AddSeenForm(form, field_types, field_types); + + // Simulate an Autofill query on a credit card field. + { + base::UserActionTester user_action_tester; + autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF()); + EXPECT_EQ(1, user_action_tester.GetActionCount( + "Autofill_PolledCreditCardSuggestions")); + } + + // Simulate submitting the credit card form. + { + base::HistogramTester histograms; + autofill_manager_->SubmitForm(form, TimeTicks::Now()); + histograms.ExpectBucketCount( + "Autofill.FormEvents.CreditCard", + AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1); + histograms.ExpectBucketCount( + "Autofill.FormEvents.CreditCard", + AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1); + // Check that the nonsecure histogram was not recorded. ExpectBucketCount() + // can't be used here because it expects the histogram to exist. + EXPECT_EQ( + 0, histograms.GetTotalCountsForPrefix("Autofill.FormEvents.CreditCard") + ["Autofill.FormEvents.CreditCard.OnNonsecurePage"]); + } +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_popup_delegate.h b/chromium/components/autofill/core/browser/autofill_popup_delegate.h index 99a66758a30..5a864242755 100644 --- a/chromium/components/autofill/core/browser/autofill_popup_delegate.h +++ b/chromium/components/autofill/core/browser/autofill_popup_delegate.h @@ -43,6 +43,9 @@ class AutofillPopupDelegate { // Informs the delegate that the Autofill previewed form should be cleared. virtual void ClearPreviewedForm() = 0; + + // Returns true if popup is for credit card. + virtual bool IsCreditCardPopup() = 0; }; } // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_profile.cc b/chromium/components/autofill/core/browser/autofill_profile.cc index 469111e4ce6..9630d5b188b 100644 --- a/chromium/components/autofill/core/browser/autofill_profile.cc +++ b/chromium/components/autofill/core/browser/autofill_profile.cc @@ -195,22 +195,23 @@ AutofillProfile::AutofillProfile(const std::string& guid, const std::string& origin) : AutofillDataModel(guid, origin), record_type_(LOCAL_PROFILE), - phone_number_(this) { -} + phone_number_(this), + has_converted_(false) {} AutofillProfile::AutofillProfile(RecordType type, const std::string& server_id) : AutofillDataModel(base::GenerateGUID(), std::string()), record_type_(type), phone_number_(this), - server_id_(server_id) { + server_id_(server_id), + has_converted_(false) { DCHECK(type == SERVER_PROFILE); } AutofillProfile::AutofillProfile() : AutofillDataModel(base::GenerateGUID(), std::string()), record_type_(LOCAL_PROFILE), - phone_number_(this) { -} + phone_number_(this), + has_converted_(false) {} AutofillProfile::AutofillProfile(const AutofillProfile& profile) : AutofillDataModel(std::string(), std::string()), phone_number_(this) { @@ -243,6 +244,7 @@ AutofillProfile& AutofillProfile::operator=(const AutofillProfile& profile) { set_language_code(profile.language_code()); server_id_ = profile.server_id(); + has_converted_ = profile.has_converted(); return *this; } diff --git a/chromium/components/autofill/core/browser/autofill_profile.h b/chromium/components/autofill/core/browser/autofill_profile.h index 00be04e8592..ae7b521385d 100644 --- a/chromium/components/autofill/core/browser/autofill_profile.h +++ b/chromium/components/autofill/core/browser/autofill_profile.h @@ -178,6 +178,10 @@ class AutofillProfile : public AutofillDataModel { // use. void RecordAndLogUse(); + // Valid only when type() == SERVER_PROFILE. + bool has_converted() const { return has_converted_; } + void set_has_converted(bool has_converted) { has_converted_ = has_converted; } + private: typedef std::vector<const FormGroup*> FormGroupList; @@ -221,6 +225,10 @@ class AutofillProfile : public AutofillDataModel { // ID used for identifying this profile. Only set for SERVER_PROFILEs. This is // a hash of the contents. std::string server_id_; + + // Only useful for SERVER_PROFILEs. Whether this server profile has been + // converted to a local profile. + bool has_converted_; }; // So we can compare AutofillProfiles with EXPECT_EQ(). diff --git a/chromium/components/autofill/core/browser/autofill_profile_comparator.cc b/chromium/components/autofill/core/browser/autofill_profile_comparator.cc index edfe2450d67..c3e8709549b 100644 --- a/chromium/components/autofill/core/browser/autofill_profile_comparator.cc +++ b/chromium/components/autofill/core/browser/autofill_profile_comparator.cc @@ -316,7 +316,7 @@ bool AutofillProfileComparator::MergeCompanyNames( switch (result) { case DIFFERENT_TOKENS: default: - NOTREACHED(); + NOTREACHED() << "Unexpected mismatch: '" << c1 << "' vs '" << c2 << "'"; return false; case S1_CONTAINS_S2: best = &c1; @@ -501,13 +501,72 @@ bool AutofillProfileComparator::MergeAddresses(const AutofillProfile& p1, break; case DIFFERENT_TOKENS: default: - // The addresses aren't mergeable and we shouldn't be doing any of + // The cities aren't mergeable and we shouldn't be doing any of + // this. + NOTREACHED() << "Unexpected mismatch: '" << city1 << "' vs '" << city2 + << "'"; + return false; + } + } + + // One of the dependend localities is empty or one of the localities has a + // subset of tokens from the other. Pick the locality name with more tokens; + // this is usually the most explicit one. + const AutofillType kDependentLocality(ADDRESS_HOME_DEPENDENT_LOCALITY); + const base::string16& locality1 = p1.GetInfo(kDependentLocality, app_locale_); + const base::string16& locality2 = p2.GetInfo(kDependentLocality, app_locale_); + if (locality1.empty()) { + address->SetInfo(kDependentLocality, locality2, app_locale_); + } else if (locality2.empty()) { + address->SetInfo(kDependentLocality, locality1, app_locale_); + } else { + // Prefer the one with more tokens, making sure to apply address + // normalization and rewriting before doing the comparison. + CompareTokensResult result = + CompareTokens(rewriter.Rewrite(NormalizeForComparison(locality1)), + rewriter.Rewrite(NormalizeForComparison(locality2))); + switch (result) { + case SAME_TOKENS: + // They have the same set of unique tokens. Let's pick the more recently + // used one. + address->SetInfo( + kDependentLocality, + (p2.use_date() > p1.use_date() ? locality2 : locality1), + app_locale_); + break; + case S1_CONTAINS_S2: + // locality1 has more unique tokens than locality2. + address->SetInfo(kDependentLocality, locality1, app_locale_); + break; + case S2_CONTAINS_S1: + // locality2 has more unique tokens than locality1. + address->SetInfo(kDependentLocality, locality2, app_locale_); + break; + case DIFFERENT_TOKENS: + default: + // The localities aren't mergeable and we shouldn't be doing any of // this. - NOTREACHED(); + NOTREACHED() << "Unexpected mismatch: '" << locality1 << "' vs '" + << locality2 << "'"; return false; } } + // One of the sorting codes is empty, they are the same, or one is a substring + // of the other. We prefer the most recently used sorting code. + const AutofillType kSortingCode(ADDRESS_HOME_SORTING_CODE); + const base::string16& sorting1 = p1.GetInfo(kSortingCode, app_locale_); + const base::string16& sorting2 = p2.GetInfo(kSortingCode, app_locale_); + if (sorting1.empty()) { + address->SetInfo(kSortingCode, sorting2, app_locale_); + } else if (sorting2.empty()) { + address->SetInfo(kSortingCode, sorting1, app_locale_); + } else { + address->SetInfo(kSortingCode, + (p2.use_date() > p1.use_date() ? sorting2 : sorting1), + app_locale_); + } + // One of the addresses is empty or one of the addresses has a subset of // tokens from the other. Prefer the more verbosely expressed one. const AutofillType kStreetAddress(ADDRESS_HOME_STREET_ADDRESS); @@ -554,7 +613,8 @@ bool AutofillProfileComparator::MergeAddresses(const AutofillProfile& p1, default: // The addresses aren't mergeable and we shouldn't be doing any of // this. - NOTREACHED(); + NOTREACHED() << "Unexpected mismatch: '" << address1 << "' vs '" + << address2 << "'"; return false; } } @@ -771,17 +831,17 @@ bool AutofillProfileComparator::HaveMergeablePhoneNumbers( // Parse and compare the phone numbers. PhoneNumberUtil* phone_util = PhoneNumberUtil::GetInstance(); switch (phone_util->IsNumberMatchWithTwoStrings(phone_1, phone_2)) { - case PhoneNumberUtil::INVALID_NUMBER: - case PhoneNumberUtil::NO_MATCH: - return false; case PhoneNumberUtil::SHORT_NSN_MATCH: case PhoneNumberUtil::NSN_MATCH: case PhoneNumberUtil::EXACT_MATCH: return true; + case PhoneNumberUtil::INVALID_NUMBER: + case PhoneNumberUtil::NO_MATCH: + return false; + default: + NOTREACHED(); + return false; } - - NOTREACHED(); - return false; } bool AutofillProfileComparator::HaveMergeableAddresses( @@ -797,10 +857,6 @@ bool AutofillProfileComparator::HaveMergeableAddresses( return false; } - // TODO(rogerm): Lookup the normalization rules for the (common) country of - // the address. The rules should be applied post NormalizeForComparison to - // the state, city, and address bag of words comparisons. - // Zip // ---- // If the addresses are definitely not in the same zip/area code then we're @@ -816,6 +872,8 @@ bool AutofillProfileComparator::HaveMergeableAddresses( return false; } + // Use the token rewrite rules for the (common) country of the address to + // transform equivalent substrings to a representative token for comparison. AddressRewriter rewriter = AddressRewriter::ForCountryCode(country1.empty() ? country2 : country1); @@ -845,14 +903,44 @@ bool AutofillProfileComparator::HaveMergeableAddresses( // TODO(rogerm): If the match is between non-empty zip codes then we can infer // that the two city strings are intended to have the same meaning. This // handles the cases where we have a city vs one of its suburbs. - const base::string16& city1 = rewriter.Rewrite(NormalizeForComparison( - p1.GetInfo(AutofillType(ADDRESS_HOME_CITY), app_locale_))); - const base::string16& city2 = rewriter.Rewrite(NormalizeForComparison( - p2.GetInfo(AutofillType(ADDRESS_HOME_CITY), app_locale_))); + const AutofillType kCity(ADDRESS_HOME_CITY); + const base::string16& city1 = + rewriter.Rewrite(NormalizeForComparison(p1.GetInfo(kCity, app_locale_))); + const base::string16& city2 = + rewriter.Rewrite(NormalizeForComparison(p2.GetInfo(kCity, app_locale_))); if (CompareTokens(city1, city2) == DIFFERENT_TOKENS) { return false; } + // Dependent Locality + // ------------------- + // Heuristic: Dependent Localities are mergeable if one is a (possibly empty) + // bag of words subset of the other. + const AutofillType kDependentLocality(ADDRESS_HOME_DEPENDENT_LOCALITY); + const base::string16& locality1 = rewriter.Rewrite( + NormalizeForComparison(p1.GetInfo(kDependentLocality, app_locale_))); + const base::string16& locality2 = rewriter.Rewrite( + NormalizeForComparison(p2.GetInfo(kDependentLocality, app_locale_))); + if (CompareTokens(locality1, locality2) == DIFFERENT_TOKENS) { + return false; + } + + // Sorting Code + // ------------- + // Heuristic: Sorting codes are mergeable if one is empty or one is a + // substring of the other, post normalization and whitespace removed. This + // is similar to postal/zip codes. + const AutofillType kSortingCode(ADDRESS_HOME_SORTING_CODE); + const base::string16& sorting1 = NormalizeForComparison( + p1.GetInfo(kSortingCode, app_locale_), DISCARD_WHITESPACE); + const base::string16& sorting2 = NormalizeForComparison( + p2.GetInfo(kSortingCode, app_locale_), DISCARD_WHITESPACE); + if (!sorting1.empty() && !sorting2.empty() && + sorting1.find(sorting2) == base::string16::npos && + sorting2.find(sorting1) == base::string16::npos) { + return false; + } + // Address // -------- // Heuristic: Street addresses are mergeable if one is a (possibly empty) bag diff --git a/chromium/components/autofill/core/browser/autofill_profile_comparator_unittest.cc b/chromium/components/autofill/core/browser/autofill_profile_comparator_unittest.cc index bcd0274eb66..36da4fdf765 100644 --- a/chromium/components/autofill/core/browser/autofill_profile_comparator_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_profile_comparator_unittest.cc @@ -16,9 +16,11 @@ // Field Type Constants using autofill::ADDRESS_HOME_CITY; using autofill::ADDRESS_HOME_COUNTRY; +using autofill::ADDRESS_HOME_DEPENDENT_LOCALITY; using autofill::ADDRESS_HOME_LINE1; using autofill::ADDRESS_HOME_LINE2; using autofill::ADDRESS_HOME_LINE3; +using autofill::ADDRESS_HOME_SORTING_CODE; using autofill::ADDRESS_HOME_STATE; using autofill::ADDRESS_HOME_STREET_ADDRESS; using autofill::ADDRESS_HOME_ZIP; @@ -239,6 +241,13 @@ class AutofillProfileComparatorTest : public ::testing::Test { EXPECT_EQ( expected.GetInfo(AutofillType(ADDRESS_HOME_STREET_ADDRESS), kLocale), actual.GetInfo(AutofillType(ADDRESS_HOME_STREET_ADDRESS), kLocale)); + EXPECT_EQ( + expected.GetInfo(AutofillType(ADDRESS_HOME_DEPENDENT_LOCALITY), + kLocale), + actual.GetInfo(AutofillType(ADDRESS_HOME_DEPENDENT_LOCALITY), kLocale)); + EXPECT_EQ( + expected.GetInfo(AutofillType(ADDRESS_HOME_SORTING_CODE), kLocale), + actual.GetInfo(AutofillType(ADDRESS_HOME_SORTING_CODE), kLocale)); EXPECT_EQ(expected.GetInfo(AutofillType(ADDRESS_HOME_CITY), kLocale), actual.GetInfo(AutofillType(ADDRESS_HOME_CITY), kLocale)); EXPECT_EQ(expected.GetInfo(AutofillType(ADDRESS_HOME_STATE), kLocale), @@ -518,10 +527,14 @@ TEST_F(AutofillProfileComparatorTest, HaveMergeableAddresses) { AutofillProfile empty = CreateProfileWithAddress("", "", "", "", "", ""); AutofillProfile p1 = CreateProfileWithAddress( "1 Some Street", "Unit 3", "Carver", "CA - California", "90210", "US"); + p1.SetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY, UTF8ToUTF16("Some String")); + p1.SetRawInfo(ADDRESS_HOME_SORTING_CODE, UTF8ToUTF16("64205 Biarritz CEDEX")); + AutofillProfile p2 = CreateProfileWithAddress( "Unit 3", "1 Some Street", "Suburb", "california", "90 210-3214", ""); AutofillProfile p3 = CreateProfileWithAddress("1 Some Street #3", "", "Carver City", "ca", "", "us"); + AutofillProfile differentCountry = CopyAndModify(p1, {{ADDRESS_HOME_COUNTRY, "CA"}}); AutofillProfile differentZip = @@ -533,6 +546,10 @@ TEST_F(AutofillProfileComparatorTest, HaveMergeableAddresses) { AutofillProfile differentAddress = CopyAndModify(p1, {{ADDRESS_HOME_LINE1, "17 Park Lane"}, {ADDRESS_HOME_LINE2, "Suite 150"}}); + AutofillProfile differentLocality = + CopyAndModify(p1, {{ADDRESS_HOME_DEPENDENT_LOCALITY, "Funky Chicken"}}); + AutofillProfile differentSortingCode = + CopyAndModify(p1, {{ADDRESS_HOME_SORTING_CODE, "98000 Monaco"}}); EXPECT_TRUE(comparator_.HaveMergeableAddresses(p1, empty)); EXPECT_TRUE(comparator_.HaveMergeableAddresses(empty, p2)); @@ -553,6 +570,8 @@ TEST_F(AutofillProfileComparatorTest, HaveMergeableAddresses) { EXPECT_FALSE(comparator_.HaveMergeableAddresses(p1, differentState)); EXPECT_FALSE(comparator_.HaveMergeableAddresses(p1, differentCity)); EXPECT_FALSE(comparator_.HaveMergeableAddresses(p1, differentAddress)); + EXPECT_FALSE(comparator_.HaveMergeableAddresses(p1, differentLocality)); + EXPECT_FALSE(comparator_.HaveMergeableAddresses(p1, differentSortingCode)); } TEST_F(AutofillProfileComparatorTest, AreMergeable) { @@ -902,7 +921,6 @@ TEST_F(AutofillProfileComparatorTest, MergePhoneNumbers_Intl) { } TEST_F(AutofillProfileComparatorTest, MergeAddresses) { - AutofillProfile empty; AutofillProfile p1 = CreateProfileWithAddress( "1 Some Street", "Unit 3", "Carver", "CA - California", "90210", "US"); AutofillProfile p2 = CreateProfileWithAddress( @@ -919,8 +937,25 @@ TEST_F(AutofillProfileComparatorTest, MergeAddresses) { MergeAddressesAndExpect(p1, p2, expected); } +TEST_F(AutofillProfileComparatorTest, MergeAddressesMostUniqueTokens) { + AutofillProfile p1 = CreateProfileWithAddress( + "1 Some Street", "Unit 3", "Carver", "CA - California", "90210", "US"); + AutofillProfile p2 = CreateProfileWithAddress( + "1 Some Other Street", "Unit 3", "Carver City", "ca", "90210-1234", "us"); + + Address expected; + expected.SetRawInfo(ADDRESS_HOME_LINE1, UTF8ToUTF16("1 Some Other Street")); + expected.SetRawInfo(ADDRESS_HOME_LINE2, UTF8ToUTF16("Unit 3")); + expected.SetRawInfo(ADDRESS_HOME_CITY, UTF8ToUTF16("Carver City")); + expected.SetRawInfo(ADDRESS_HOME_STATE, UTF8ToUTF16("ca")); + expected.SetRawInfo(ADDRESS_HOME_ZIP, UTF8ToUTF16("90210-1234")); + expected.SetRawInfo(ADDRESS_HOME_COUNTRY, UTF8ToUTF16("US")); + + MergeAddressesAndExpect(p1, p2, expected); + MergeAddressesAndExpect(p2, p1, expected); +} + TEST_F(AutofillProfileComparatorTest, MergeAddressesWithRewrite) { - AutofillProfile empty; AutofillProfile p1 = CreateProfileWithAddress( "6543 CH BACON", "APP 3", "MONTRÉAL", "QUÉBEC", "HHH999", "ca"); AutofillProfile p2 = CreateProfileWithAddress( @@ -936,4 +971,34 @@ TEST_F(AutofillProfileComparatorTest, MergeAddressesWithRewrite) { expected.SetRawInfo(ADDRESS_HOME_COUNTRY, UTF8ToUTF16("CA")); MergeAddressesAndExpect(p1, p2, expected); + MergeAddressesAndExpect(p2, p1, expected); +} + +TEST_F(AutofillProfileComparatorTest, + MergeAddressesDependendLocalityAndSortingCode) { + AutofillProfile p1 = CreateProfileWithAddress( + "6543 CH BACON", "APP 3", "MONTRÉAL", "QUÉBEC", "HHH999", "ca"); + p1.SetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY, UTF8ToUTF16("Some String")); + p1.SetRawInfo(ADDRESS_HOME_SORTING_CODE, UTF8ToUTF16("64205 Biarritz CEDEX")); + AutofillProfile p2 = CreateProfileWithAddress( + "6543, Bacon Rd", "", "Montreal", "QC", "hhh 999", "CA"); + p2.SetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY, + UTF8ToUTF16("Some Other String")); + p2.SetRawInfo(ADDRESS_HOME_SORTING_CODE, UTF8ToUTF16("64205 Biarritz")); + p2.set_use_date(p1.use_date() + base::TimeDelta::FromMinutes(1)); + + Address expected; + expected.SetRawInfo(ADDRESS_HOME_LINE1, UTF8ToUTF16("6543 CH BACON")); + expected.SetRawInfo(ADDRESS_HOME_LINE2, UTF8ToUTF16("APP 3")); + expected.SetRawInfo(ADDRESS_HOME_CITY, UTF8ToUTF16("Montreal")); + expected.SetRawInfo(ADDRESS_HOME_STATE, UTF8ToUTF16("QC")); + expected.SetRawInfo(ADDRESS_HOME_ZIP, UTF8ToUTF16("hhh 999")); + expected.SetRawInfo(ADDRESS_HOME_COUNTRY, UTF8ToUTF16("CA")); + expected.SetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY, + UTF8ToUTF16("Some Other String")); + expected.SetRawInfo(ADDRESS_HOME_SORTING_CODE, + UTF8ToUTF16("64205 Biarritz")); // Preferred by use date. + + MergeAddressesAndExpect(p1, p2, expected); + MergeAddressesAndExpect(p2, p1, expected); } diff --git a/chromium/components/autofill/core/browser/autofill_profile_unittest.cc b/chromium/components/autofill/core/browser/autofill_profile_unittest.cc index d50ddecf7ee..c54f2ae61c4 100644 --- a/chromium/components/autofill/core/browser/autofill_profile_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_profile_unittest.cc @@ -7,11 +7,12 @@ #include <stddef.h> #include <memory> +#include <vector> #include "base/format_macros.h" #include "base/guid.h" #include "base/macros.h" -#include "base/memory/scoped_vector.h" +#include "base/memory/ptr_util.h" #include "base/stl_util.h" #include "base/strings/string16.h" #include "base/strings/stringprintf.h" @@ -79,6 +80,14 @@ void SetupTestProfile(AutofillProfile& profile) { "Hollywood", "CA", "91601", "US", "12345678910"); } +std::vector<AutofillProfile*> ToRawPointerVector( + const std::vector<std::unique_ptr<AutofillProfile>>& list) { + std::vector<AutofillProfile*> result; + for (const auto& item : list) + result.push_back(item.get()); + return result; +} + } // namespace // Tests different possibilities for summary string generation. @@ -186,62 +195,32 @@ TEST(AutofillProfileTest, PreviewSummaryString) { } TEST(AutofillProfileTest, AdjustInferredLabels) { - ScopedVector<AutofillProfile> profiles; - profiles.push_back( - new AutofillProfile(base::GenerateGUID(), "https://www.example.com/")); - test::SetProfileInfo( - profiles[0], - "John", - "", - "Doe", - "johndoe@hades.com", - "Underworld", - "666 Erebus St.", - "", - "Elysium", "CA", - "91111", - "US", - "16502111111"); - profiles.push_back( - new AutofillProfile(base::GenerateGUID(), "http://www.example.com/")); - test::SetProfileInfo( - profiles[1], - "Jane", - "", - "Doe", - "janedoe@tertium.com", - "Pluto Inc.", - "123 Letha Shore.", - "", - "Dis", "CA", - "91222", - "US", - "12345678910"); + std::vector<std::unique_ptr<AutofillProfile>> profiles; + profiles.push_back(base::MakeUnique<AutofillProfile>( + base::GenerateGUID(), "https://www.example.com/")); + test::SetProfileInfo(profiles[0].get(), "John", "", "Doe", + "johndoe@hades.com", "Underworld", "666 Erebus St.", "", + "Elysium", "CA", "91111", "US", "16502111111"); + profiles.push_back(base::MakeUnique<AutofillProfile>( + base::GenerateGUID(), "http://www.example.com/")); + test::SetProfileInfo(profiles[1].get(), "Jane", "", "Doe", + "janedoe@tertium.com", "Pluto Inc.", "123 Letha Shore.", + "", "Dis", "CA", "91222", "US", "12345678910"); std::vector<base::string16> labels; - AutofillProfile::CreateDifferentiatingLabels( - profiles.get(), "en-US", &labels); + AutofillProfile::CreateDifferentiatingLabels(ToRawPointerVector(profiles), + "en-US", &labels); ASSERT_EQ(2U, labels.size()); EXPECT_EQ(ASCIIToUTF16("John Doe, 666 Erebus St."), labels[0]); EXPECT_EQ(ASCIIToUTF16("Jane Doe, 123 Letha Shore."), labels[1]); profiles.push_back( - new AutofillProfile(base::GenerateGUID(), kSettingsOrigin)); - test::SetProfileInfo( - profiles[2], - "John", - "", - "Doe", - "johndoe@tertium.com", - "Underworld", - "666 Erebus St.", - "", - "Elysium", "CA", - "91111", - "US", - "16502111111"); + base::MakeUnique<AutofillProfile>(base::GenerateGUID(), kSettingsOrigin)); + test::SetProfileInfo(profiles[2].get(), "John", "", "Doe", + "johndoe@tertium.com", "Underworld", "666 Erebus St.", + "", "Elysium", "CA", "91111", "US", "16502111111"); labels.clear(); - AutofillProfile::CreateDifferentiatingLabels( - profiles.get(), "en-US", &labels); + AutofillProfile::CreateDifferentiatingLabels(ToRawPointerVector(profiles), + "en-US", &labels); // Profile 0 and 2 inferred label now includes an e-mail. ASSERT_EQ(3U, labels.size()); @@ -254,24 +233,15 @@ TEST(AutofillProfileTest, AdjustInferredLabels) { profiles.resize(2); profiles.push_back( - new AutofillProfile(base::GenerateGUID(), std::string())); - test::SetProfileInfo( - profiles[2], - "John", - "", - "Doe", - "johndoe@hades.com", - "Underworld", - "666 Erebus St.", - "", - "Elysium", "CO", // State is different - "91111", - "US", - "16502111111"); + base::MakeUnique<AutofillProfile>(base::GenerateGUID(), std::string())); + test::SetProfileInfo(profiles[2].get(), "John", "", "Doe", + "johndoe@hades.com", "Underworld", "666 Erebus St.", "", + "Elysium", "CO", // State is different + "91111", "US", "16502111111"); labels.clear(); - AutofillProfile::CreateDifferentiatingLabels( - profiles.get(), "en-US", &labels); + AutofillProfile::CreateDifferentiatingLabels(ToRawPointerVector(profiles), + "en-US", &labels); // Profile 0 and 2 inferred label now includes a state. ASSERT_EQ(3U, labels.size()); @@ -279,25 +249,17 @@ TEST(AutofillProfileTest, AdjustInferredLabels) { EXPECT_EQ(ASCIIToUTF16("Jane Doe, 123 Letha Shore."), labels[1]); EXPECT_EQ(ASCIIToUTF16("John Doe, 666 Erebus St., CO"), labels[2]); - profiles.push_back( - new AutofillProfile(base::GenerateGUID(), "https://www.example.com/")); - test::SetProfileInfo( - profiles[3], - "John", - "", - "Doe", - "johndoe@hades.com", - "Underworld", - "666 Erebus St.", - "", - "Elysium", "CO", // State is different for some. - "91111", - "US", - "16504444444"); // Phone is different for some. + profiles.push_back(base::MakeUnique<AutofillProfile>( + base::GenerateGUID(), "https://www.example.com/")); + test::SetProfileInfo(profiles[3].get(), "John", "", "Doe", + "johndoe@hades.com", "Underworld", "666 Erebus St.", "", + "Elysium", "CO", // State is different for some. + "91111", "US", + "16504444444"); // Phone is different for some. labels.clear(); - AutofillProfile::CreateDifferentiatingLabels( - profiles.get(), "en-US", &labels); + AutofillProfile::CreateDifferentiatingLabels(ToRawPointerVector(profiles), + "en-US", &labels); ASSERT_EQ(4U, labels.size()); EXPECT_EQ(ASCIIToUTF16("John Doe, 666 Erebus St., CA"), labels[0]); EXPECT_EQ(ASCIIToUTF16("Jane Doe, 123 Letha Shore."), labels[1]); @@ -308,25 +270,18 @@ TEST(AutofillProfileTest, AdjustInferredLabels) { EXPECT_EQ(ASCIIToUTF16("John Doe, 666 Erebus St., CO, 16504444444"), labels[3]); - profiles.push_back( - new AutofillProfile(base::GenerateGUID(), "https://www.example.com/")); - test::SetProfileInfo( - profiles[4], - "John", - "", - "Doe", - "johndoe@styx.com", // E-Mail is different for some. - "Underworld", - "666 Erebus St.", - "", - "Elysium", "CO", // State is different for some. - "91111", - "US", - "16504444444"); // Phone is different for some. + profiles.push_back(base::MakeUnique<AutofillProfile>( + base::GenerateGUID(), "https://www.example.com/")); + test::SetProfileInfo(profiles[4].get(), "John", "", "Doe", + "johndoe@styx.com", // E-Mail is different for some. + "Underworld", "666 Erebus St.", "", "Elysium", + "CO", // State is different for some. + "91111", "US", + "16504444444"); // Phone is different for some. labels.clear(); - AutofillProfile::CreateDifferentiatingLabels( - profiles.get(), "en-US", &labels); + AutofillProfile::CreateDifferentiatingLabels(ToRawPointerVector(profiles), + "en-US", &labels); ASSERT_EQ(5U, labels.size()); EXPECT_EQ(ASCIIToUTF16("John Doe, 666 Erebus St., CA"), labels[0]); EXPECT_EQ(ASCIIToUTF16("Jane Doe, 123 Letha Shore."), labels[1]); @@ -341,21 +296,13 @@ TEST(AutofillProfileTest, AdjustInferredLabels) { } TEST(AutofillProfileTest, CreateInferredLabelsI18n_CH) { - ScopedVector<AutofillProfile> profiles; - profiles.push_back( - new AutofillProfile(base::GenerateGUID(), "https://www.example.com/")); - test::SetProfileInfo(profiles.back(), - "H.", - "R.", - "Giger", - "hrgiger@beispiel.com", - "Beispiel Inc", - "Brandschenkestrasse 110", - "", - "Zurich", "", - "8002", - "CH", - "+41 44-668-1800"); + std::vector<std::unique_ptr<AutofillProfile>> profiles; + profiles.push_back(base::MakeUnique<AutofillProfile>( + base::GenerateGUID(), "https://www.example.com/")); + test::SetProfileInfo(profiles.back().get(), "H.", "R.", "Giger", + "hrgiger@beispiel.com", "Beispiel Inc", + "Brandschenkestrasse 110", "", "Zurich", "", "8002", + "CH", "+41 44-668-1800"); profiles.back()->set_language_code("de_CH"); static const char* kExpectedLabels[] = { "", @@ -374,8 +321,8 @@ TEST(AutofillProfileTest, CreateInferredLabelsI18n_CH) { std::vector<base::string16> labels; for (size_t i = 0; i < arraysize(kExpectedLabels); ++i) { - AutofillProfile::CreateInferredLabels( - profiles.get(), NULL, UNKNOWN_TYPE, i, "en-US", &labels); + AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles), NULL, + UNKNOWN_TYPE, i, "en-US", &labels); ASSERT_FALSE(labels.empty()); EXPECT_EQ(UTF8ToUTF16(kExpectedLabels[i]), labels.back()); } @@ -383,21 +330,12 @@ TEST(AutofillProfileTest, CreateInferredLabelsI18n_CH) { TEST(AutofillProfileTest, CreateInferredLabelsI18n_FR) { - ScopedVector<AutofillProfile> profiles; - profiles.push_back( - new AutofillProfile(base::GenerateGUID(), "https://www.example.com/")); - test::SetProfileInfo(profiles.back(), - "Antoine", - "", - "de Saint-Exupéry", - "antoine@exemple.com", - "Exemple Inc", - "8 Rue de Londres", - "", - "Paris", "", - "75009", - "FR", - "+33 (0) 1 42 68 53 00"); + std::vector<std::unique_ptr<AutofillProfile>> profiles; + profiles.push_back(base::MakeUnique<AutofillProfile>( + base::GenerateGUID(), "https://www.example.com/")); + test::SetProfileInfo(profiles.back().get(), "Antoine", "", "de Saint-Exupéry", + "antoine@exemple.com", "Exemple Inc", "8 Rue de Londres", + "", "Paris", "", "75009", "FR", "+33 (0) 1 42 68 53 00"); profiles.back()->set_language_code("fr_FR"); profiles.back()->SetInfo( AutofillType(ADDRESS_HOME_SORTING_CODE), UTF8ToUTF16("CEDEX"), "en-US"); @@ -422,29 +360,21 @@ TEST(AutofillProfileTest, CreateInferredLabelsI18n_FR) { std::vector<base::string16> labels; for (size_t i = 0; i < arraysize(kExpectedLabels); ++i) { - AutofillProfile::CreateInferredLabels( - profiles.get(), NULL, UNKNOWN_TYPE, i, "en-US", &labels); + AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles), NULL, + UNKNOWN_TYPE, i, "en-US", &labels); ASSERT_FALSE(labels.empty()); EXPECT_EQ(UTF8ToUTF16(kExpectedLabels[i]), labels.back()); } } TEST(AutofillProfileTest, CreateInferredLabelsI18n_KR) { - ScopedVector<AutofillProfile> profiles; - profiles.push_back( - new AutofillProfile(base::GenerateGUID(), "https://www.example.com/")); - test::SetProfileInfo(profiles.back(), - "Park", - "", - "Jae-sang", - "park@yeleul.com", - "Yeleul Inc", - "Gangnam Finance Center", - "152 Teheran-ro", - "Gangnam-Gu", "Seoul", - "135-984", - "KR", - "+82-2-531-9000"); + std::vector<std::unique_ptr<AutofillProfile>> profiles; + profiles.push_back(base::MakeUnique<AutofillProfile>( + base::GenerateGUID(), "https://www.example.com/")); + test::SetProfileInfo(profiles.back().get(), "Park", "", "Jae-sang", + "park@yeleul.com", "Yeleul Inc", + "Gangnam Finance Center", "152 Teheran-ro", "Gangnam-Gu", + "Seoul", "135-984", "KR", "+82-2-531-9000"); profiles.back()->set_language_code("ko_Latn"); profiles.back()->SetInfo(AutofillType(ADDRESS_HOME_DEPENDENT_LOCALITY), UTF8ToUTF16("Yeoksam-Dong"), @@ -475,29 +405,21 @@ TEST(AutofillProfileTest, CreateInferredLabelsI18n_KR) { std::vector<base::string16> labels; for (size_t i = 0; i < arraysize(kExpectedLabels); ++i) { - AutofillProfile::CreateInferredLabels( - profiles.get(), NULL, UNKNOWN_TYPE, i, "en-US", &labels); + AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles), NULL, + UNKNOWN_TYPE, i, "en-US", &labels); ASSERT_FALSE(labels.empty()); EXPECT_EQ(UTF8ToUTF16(kExpectedLabels[i]), labels.back()); } } TEST(AutofillProfileTest, CreateInferredLabelsI18n_JP_Latn) { - ScopedVector<AutofillProfile> profiles; - profiles.push_back( - new AutofillProfile(base::GenerateGUID(), "https://www.example.com/")); - test::SetProfileInfo(profiles.back(), - "Miku", - "", - "Hatsune", - "miku@rei.com", - "Rei Inc", - "Roppongi Hills Mori Tower", - "6-10-1 Roppongi", - "Minato-ku", "Tokyo", - "106-6126", - "JP", - "+81-3-6384-9000"); + std::vector<std::unique_ptr<AutofillProfile>> profiles; + profiles.push_back(base::MakeUnique<AutofillProfile>( + base::GenerateGUID(), "https://www.example.com/")); + test::SetProfileInfo(profiles.back().get(), "Miku", "", "Hatsune", + "miku@rei.com", "Rei Inc", "Roppongi Hills Mori Tower", + "6-10-1 Roppongi", "Minato-ku", "Tokyo", "106-6126", + "JP", "+81-3-6384-9000"); profiles.back()->set_language_code("ja_Latn"); static const char* kExpectedLabels[] = { "", @@ -521,28 +443,20 @@ TEST(AutofillProfileTest, CreateInferredLabelsI18n_JP_Latn) { std::vector<base::string16> labels; for (size_t i = 0; i < arraysize(kExpectedLabels); ++i) { - AutofillProfile::CreateInferredLabels( - profiles.get(), NULL, UNKNOWN_TYPE, i, "en-US", &labels); + AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles), NULL, + UNKNOWN_TYPE, i, "en-US", &labels); ASSERT_FALSE(labels.empty()); EXPECT_EQ(UTF8ToUTF16(kExpectedLabels[i]), labels.back()); } } TEST(AutofillProfileTest, CreateInferredLabelsI18n_JP_ja) { - ScopedVector<AutofillProfile> profiles; - profiles.push_back( - new AutofillProfile(base::GenerateGUID(), "https://www.example.com/")); - test::SetProfileInfo(profiles.back(), - "ミク", - "", - "初音", - "miku@rei.com", - "例", - "六本木ヒルズ森タワー", - "六本木 6-10-1", - "港区", "東京都", - "106-6126", - "JP", + std::vector<std::unique_ptr<AutofillProfile>> profiles; + profiles.push_back(base::MakeUnique<AutofillProfile>( + base::GenerateGUID(), "https://www.example.com/")); + test::SetProfileInfo(profiles.back().get(), "ミク", "", "初音", + "miku@rei.com", "例", "六本木ヒルズ森タワー", + "六本木 6-10-1", "港区", "東京都", "106-6126", "JP", "03-6384-9000"); profiles.back()->set_language_code("ja_JP"); static const char* kExpectedLabels[] = { @@ -563,53 +477,35 @@ TEST(AutofillProfileTest, CreateInferredLabelsI18n_JP_ja) { std::vector<base::string16> labels; for (size_t i = 0; i < arraysize(kExpectedLabels); ++i) { - AutofillProfile::CreateInferredLabels( - profiles.get(), NULL, UNKNOWN_TYPE, i, "en-US", &labels); + AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles), NULL, + UNKNOWN_TYPE, i, "en-US", &labels); ASSERT_FALSE(labels.empty()); EXPECT_EQ(UTF8ToUTF16(kExpectedLabels[i]), labels.back()); } } TEST(AutofillProfileTest, CreateInferredLabels) { - ScopedVector<AutofillProfile> profiles; - profiles.push_back( - new AutofillProfile(base::GenerateGUID(), "https://www.example.com/")); - test::SetProfileInfo(profiles[0], - "John", - "", - "Doe", - "johndoe@hades.com", - "Underworld", - "666 Erebus St.", - "", - "Elysium", "CA", - "91111", - "US", - "16502111111"); - profiles.push_back( - new AutofillProfile(base::GenerateGUID(), "https://www.example.com/")); - test::SetProfileInfo(profiles[1], - "Jane", - "", - "Doe", - "janedoe@tertium.com", - "Pluto Inc.", - "123 Letha Shore.", - "", - "Dis", "CA", - "91222", - "US", - "12345678910"); + std::vector<std::unique_ptr<AutofillProfile>> profiles; + profiles.push_back(base::MakeUnique<AutofillProfile>( + base::GenerateGUID(), "https://www.example.com/")); + test::SetProfileInfo(profiles[0].get(), "John", "", "Doe", + "johndoe@hades.com", "Underworld", "666 Erebus St.", "", + "Elysium", "CA", "91111", "US", "16502111111"); + profiles.push_back(base::MakeUnique<AutofillProfile>( + base::GenerateGUID(), "https://www.example.com/")); + test::SetProfileInfo(profiles[1].get(), "Jane", "", "Doe", + "janedoe@tertium.com", "Pluto Inc.", "123 Letha Shore.", + "", "Dis", "CA", "91222", "US", "12345678910"); std::vector<base::string16> labels; // Two fields at least - no filter. - AutofillProfile::CreateInferredLabels(profiles.get(), NULL, UNKNOWN_TYPE, 2, - "en-US", &labels); + AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles), NULL, + UNKNOWN_TYPE, 2, "en-US", &labels); EXPECT_EQ(ASCIIToUTF16("John Doe, 666 Erebus St."), labels[0]); EXPECT_EQ(ASCIIToUTF16("Jane Doe, 123 Letha Shore."), labels[1]); // Three fields at least - no filter. - AutofillProfile::CreateInferredLabels(profiles.get(), NULL, UNKNOWN_TYPE, 3, - "en-US", &labels); + AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles), NULL, + UNKNOWN_TYPE, 3, "en-US", &labels); EXPECT_EQ(ASCIIToUTF16("John Doe, 666 Erebus St., Elysium"), labels[0]); EXPECT_EQ(ASCIIToUTF16("Jane Doe, 123 Letha Shore., Dis"), @@ -621,21 +517,24 @@ TEST(AutofillProfileTest, CreateInferredLabels) { suggested_fields.push_back(ADDRESS_HOME_ZIP); // Two fields at least, from suggested fields - no filter. - AutofillProfile::CreateInferredLabels(profiles.get(), &suggested_fields, - UNKNOWN_TYPE, 2, "en-US", &labels); + AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles), + &suggested_fields, UNKNOWN_TYPE, 2, + "en-US", &labels); EXPECT_EQ(ASCIIToUTF16("Elysium 91111"), labels[0]); EXPECT_EQ(ASCIIToUTF16("Dis 91222"), labels[1]); // Three fields at least, from suggested fields - no filter. - AutofillProfile::CreateInferredLabels(profiles.get(), &suggested_fields, - UNKNOWN_TYPE, 3, "en-US", &labels); + AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles), + &suggested_fields, UNKNOWN_TYPE, 3, + "en-US", &labels); EXPECT_EQ(ASCIIToUTF16("Elysium, CA 91111"), labels[0]); EXPECT_EQ(ASCIIToUTF16("Dis, CA 91222"), labels[1]); // Three fields at least, from suggested fields - but filter reduces available // fields to two. - AutofillProfile::CreateInferredLabels(profiles.get(), &suggested_fields, - ADDRESS_HOME_ZIP, 3, "en-US", &labels); + AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles), + &suggested_fields, ADDRESS_HOME_ZIP, 3, + "en-US", &labels); EXPECT_EQ(ASCIIToUTF16("Elysium, CA"), labels[0]); EXPECT_EQ(ASCIIToUTF16("Dis, CA"), labels[1]); @@ -643,15 +542,17 @@ TEST(AutofillProfileTest, CreateInferredLabels) { // In our implementation we always display NAME_FULL for all NAME* fields... suggested_fields.push_back(NAME_MIDDLE); // One field at least, from suggested fields - no filter. - AutofillProfile::CreateInferredLabels(profiles.get(), &suggested_fields, - UNKNOWN_TYPE, 1, "en-US", &labels); + AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles), + &suggested_fields, UNKNOWN_TYPE, 1, + "en-US", &labels); EXPECT_EQ(ASCIIToUTF16("John Doe"), labels[0]); EXPECT_EQ(ASCIIToUTF16("Jane Doe"), labels[1]); // One field at least, from suggested fields - filter the same as suggested // field. - AutofillProfile::CreateInferredLabels(profiles.get(), &suggested_fields, - NAME_MIDDLE, 1, "en-US", &labels); + AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles), + &suggested_fields, NAME_MIDDLE, 1, + "en-US", &labels); EXPECT_EQ(base::string16(), labels[0]); EXPECT_EQ(base::string16(), labels[1]); @@ -659,8 +560,9 @@ TEST(AutofillProfileTest, CreateInferredLabels) { // In our implementation we always display NAME_FULL for NAME_MIDDLE_INITIAL suggested_fields.push_back(NAME_MIDDLE_INITIAL); // One field at least, from suggested fields - no filter. - AutofillProfile::CreateInferredLabels(profiles.get(), &suggested_fields, - UNKNOWN_TYPE, 1, "en-US", &labels); + AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles), + &suggested_fields, UNKNOWN_TYPE, 1, + "en-US", &labels); EXPECT_EQ(ASCIIToUTF16("John Doe"), labels[0]); EXPECT_EQ(ASCIIToUTF16("Jane Doe"), labels[1]); @@ -670,13 +572,14 @@ TEST(AutofillProfileTest, CreateInferredLabels) { suggested_fields.push_back(UNKNOWN_TYPE); suggested_fields.push_back(NAME_FULL); suggested_fields.push_back(ADDRESS_HOME_LINE1); - AutofillProfile::CreateInferredLabels(profiles.get(), &suggested_fields, - NAME_FULL, 1, "en-US", &labels); + AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles), + &suggested_fields, NAME_FULL, 1, + "en-US", &labels); EXPECT_EQ(base::string16(ASCIIToUTF16("666 Erebus St.")), labels[0]); EXPECT_EQ(base::string16(ASCIIToUTF16("123 Letha Shore.")), labels[1]); // No suggested fields, but non-unknown excluded field. - AutofillProfile::CreateInferredLabels(profiles.get(), NULL, + AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles), NULL, NAME_FULL, 1, "en-US", &labels); EXPECT_EQ(base::string16(ASCIIToUTF16("666 Erebus St.")), labels[0]); EXPECT_EQ(base::string16(ASCIIToUTF16("123 Letha Shore.")), labels[1]); @@ -685,17 +588,16 @@ TEST(AutofillProfileTest, CreateInferredLabels) { // Test that we fall back to using the full name if there are no other // distinguishing fields, but only if it makes sense given the suggested fields. TEST(AutofillProfileTest, CreateInferredLabelsFallsBackToFullName) { - ScopedVector<AutofillProfile> profiles; - profiles.push_back( - new AutofillProfile(base::GenerateGUID(), "https://www.example.com/")); - test::SetProfileInfo(profiles[0], - "John", "", "Doe", "doe@example.com", "", - "88 Nowhere Ave.", "", "", "", "", "", ""); - profiles.push_back( - new AutofillProfile(base::GenerateGUID(), "https://www.example.com/")); - test::SetProfileInfo(profiles[1], - "Johnny", "K", "Doe", "doe@example.com", "", - "88 Nowhere Ave.", "", "", "", "", "", ""); + std::vector<std::unique_ptr<AutofillProfile>> profiles; + profiles.push_back(base::MakeUnique<AutofillProfile>( + base::GenerateGUID(), "https://www.example.com/")); + test::SetProfileInfo(profiles[0].get(), "John", "", "Doe", "doe@example.com", + "", "88 Nowhere Ave.", "", "", "", "", "", ""); + profiles.push_back(base::MakeUnique<AutofillProfile>( + base::GenerateGUID(), "https://www.example.com/")); + test::SetProfileInfo(profiles[1].get(), "Johnny", "K", "Doe", + "doe@example.com", "", "88 Nowhere Ave.", "", "", "", "", + "", ""); // If the only name field in the suggested fields is the excluded field, we // should not fall back to the full name as a distinguishing field. @@ -704,16 +606,18 @@ TEST(AutofillProfileTest, CreateInferredLabelsFallsBackToFullName) { suggested_fields.push_back(ADDRESS_HOME_LINE1); suggested_fields.push_back(EMAIL_ADDRESS); std::vector<base::string16> labels; - AutofillProfile::CreateInferredLabels(profiles.get(), &suggested_fields, - NAME_LAST, 1, "en-US", &labels); + AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles), + &suggested_fields, NAME_LAST, 1, + "en-US", &labels); ASSERT_EQ(2U, labels.size()); EXPECT_EQ(ASCIIToUTF16("88 Nowhere Ave."), labels[0]); EXPECT_EQ(ASCIIToUTF16("88 Nowhere Ave."), labels[1]); // Otherwise, we should. suggested_fields.push_back(NAME_FIRST); - AutofillProfile::CreateInferredLabels(profiles.get(), &suggested_fields, - NAME_LAST, 1, "en-US", &labels); + AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles), + &suggested_fields, NAME_LAST, 1, + "en-US", &labels); ASSERT_EQ(2U, labels.size()); EXPECT_EQ(ASCIIToUTF16("88 Nowhere Ave., John Doe"), labels[0]); EXPECT_EQ(ASCIIToUTF16("88 Nowhere Ave., Johnny K Doe"), labels[1]); @@ -721,17 +625,15 @@ TEST(AutofillProfileTest, CreateInferredLabelsFallsBackToFullName) { // Test that we do not show duplicate fields in the labels. TEST(AutofillProfileTest, CreateInferredLabelsNoDuplicatedFields) { - ScopedVector<AutofillProfile> profiles; - profiles.push_back( - new AutofillProfile(base::GenerateGUID(), "https://www.example.com/")); - test::SetProfileInfo(profiles[0], - "John", "", "Doe", "doe@example.com", "", - "88 Nowhere Ave.", "", "", "", "", "", ""); - profiles.push_back( - new AutofillProfile(base::GenerateGUID(), "https://www.example.com/")); - test::SetProfileInfo(profiles[1], - "John", "", "Doe", "dojo@example.com", "", - "88 Nowhere Ave.", "", "", "", "", "", ""); + std::vector<std::unique_ptr<AutofillProfile>> profiles; + profiles.push_back(base::MakeUnique<AutofillProfile>( + base::GenerateGUID(), "https://www.example.com/")); + test::SetProfileInfo(profiles[0].get(), "John", "", "Doe", "doe@example.com", + "", "88 Nowhere Ave.", "", "", "", "", "", ""); + profiles.push_back(base::MakeUnique<AutofillProfile>( + base::GenerateGUID(), "https://www.example.com/")); + test::SetProfileInfo(profiles[1].get(), "John", "", "Doe", "dojo@example.com", + "", "88 Nowhere Ave.", "", "", "", "", "", ""); // If the only name field in the suggested fields is the excluded field, we // should not fall back to the full name as a distinguishing field. @@ -740,8 +642,9 @@ TEST(AutofillProfileTest, CreateInferredLabelsNoDuplicatedFields) { suggested_fields.push_back(ADDRESS_BILLING_LINE1); suggested_fields.push_back(EMAIL_ADDRESS); std::vector<base::string16> labels; - AutofillProfile::CreateInferredLabels(profiles.get(), &suggested_fields, - UNKNOWN_TYPE, 2, "en-US", &labels); + AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles), + &suggested_fields, UNKNOWN_TYPE, 2, + "en-US", &labels); ASSERT_EQ(2U, labels.size()); EXPECT_EQ(ASCIIToUTF16("88 Nowhere Ave., doe@example.com"), labels[0]); EXPECT_EQ(ASCIIToUTF16("88 Nowhere Ave., dojo@example.com"), labels[1]); @@ -749,26 +652,24 @@ TEST(AutofillProfileTest, CreateInferredLabelsNoDuplicatedFields) { // Make sure that empty fields are not treated as distinguishing fields. TEST(AutofillProfileTest, CreateInferredLabelsSkipsEmptyFields) { - ScopedVector<AutofillProfile> profiles; - profiles.push_back( - new AutofillProfile(base::GenerateGUID(), "https://www.example.com/")); - test::SetProfileInfo(profiles[0], - "John", "", "Doe", "doe@example.com", + std::vector<std::unique_ptr<AutofillProfile>> profiles; + profiles.push_back(base::MakeUnique<AutofillProfile>( + base::GenerateGUID(), "https://www.example.com/")); + test::SetProfileInfo(profiles[0].get(), "John", "", "Doe", "doe@example.com", "Gogole", "", "", "", "", "", "", ""); - profiles.push_back( - new AutofillProfile(base::GenerateGUID(), "https://www.example.com/")); - test::SetProfileInfo(profiles[1], - "John", "", "Doe", "doe@example.com", + profiles.push_back(base::MakeUnique<AutofillProfile>( + base::GenerateGUID(), "https://www.example.com/")); + test::SetProfileInfo(profiles[1].get(), "John", "", "Doe", "doe@example.com", "Ggoole", "", "", "", "", "", "", ""); - profiles.push_back( - new AutofillProfile(base::GenerateGUID(), "https://www.example.com/")); - test::SetProfileInfo(profiles[2], - "John", "", "Doe", "john.doe@example.com", - "Goolge", "", "", "", "", "", "", ""); + profiles.push_back(base::MakeUnique<AutofillProfile>( + base::GenerateGUID(), "https://www.example.com/")); + test::SetProfileInfo(profiles[2].get(), "John", "", "Doe", + "john.doe@example.com", "Goolge", "", "", "", "", "", "", + ""); std::vector<base::string16> labels; - AutofillProfile::CreateInferredLabels(profiles.get(), NULL, UNKNOWN_TYPE, 3, - "en-US", &labels); + AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles), NULL, + UNKNOWN_TYPE, 3, "en-US", &labels); ASSERT_EQ(3U, labels.size()); EXPECT_EQ(ASCIIToUTF16("John Doe, doe@example.com, Gogole"), labels[0]); EXPECT_EQ(ASCIIToUTF16("John Doe, doe@example.com, Ggoole"), labels[1]); @@ -777,8 +678,8 @@ TEST(AutofillProfileTest, CreateInferredLabelsSkipsEmptyFields) { // A field must have a non-empty value for each profile to be considered a // distinguishing field. profiles[1]->SetRawInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("88 Nowhere Ave.")); - AutofillProfile::CreateInferredLabels(profiles.get(), NULL, UNKNOWN_TYPE, 1, - "en-US", &labels); + AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles), NULL, + UNKNOWN_TYPE, 1, "en-US", &labels); ASSERT_EQ(3U, labels.size()); EXPECT_EQ(ASCIIToUTF16("John Doe, doe@example.com, Gogole"), labels[0]); EXPECT_EQ(ASCIIToUTF16("John Doe, 88 Nowhere Ave., doe@example.com, Ggoole"), @@ -788,12 +689,11 @@ TEST(AutofillProfileTest, CreateInferredLabelsSkipsEmptyFields) { // Test that labels that would otherwise have multiline values are flattened. TEST(AutofillProfileTest, CreateInferredLabelsFlattensMultiLineValues) { - ScopedVector<AutofillProfile> profiles; - profiles.push_back( - new AutofillProfile(base::GenerateGUID(), "https://www.example.com/")); - test::SetProfileInfo(profiles[0], - "John", "", "Doe", "doe@example.com", "", - "88 Nowhere Ave.", "Apt. 42", "", "", "", "", ""); + std::vector<std::unique_ptr<AutofillProfile>> profiles; + profiles.push_back(base::MakeUnique<AutofillProfile>( + base::GenerateGUID(), "https://www.example.com/")); + test::SetProfileInfo(profiles[0].get(), "John", "", "Doe", "doe@example.com", + "", "88 Nowhere Ave.", "Apt. 42", "", "", "", "", ""); // If the only name field in the suggested fields is the excluded field, we // should not fall back to the full name as a distinguishing field. @@ -801,8 +701,9 @@ TEST(AutofillProfileTest, CreateInferredLabelsFlattensMultiLineValues) { suggested_fields.push_back(NAME_FULL); suggested_fields.push_back(ADDRESS_HOME_STREET_ADDRESS); std::vector<base::string16> labels; - AutofillProfile::CreateInferredLabels(profiles.get(), &suggested_fields, - NAME_FULL, 1, "en-US", &labels); + AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles), + &suggested_fields, NAME_FULL, 1, + "en-US", &labels); ASSERT_EQ(1U, labels.size()); EXPECT_EQ(ASCIIToUTF16("88 Nowhere Ave., Apt. 42"), labels[0]); } diff --git a/chromium/components/autofill/core/browser/autofill_scanner.cc b/chromium/components/autofill/core/browser/autofill_scanner.cc index 47a8c4c989e..857a7b8987f 100644 --- a/chromium/components/autofill/core/browser/autofill_scanner.cc +++ b/chromium/components/autofill/core/browser/autofill_scanner.cc @@ -9,11 +9,16 @@ namespace autofill { -AutofillScanner::AutofillScanner(const std::vector<AutofillField*>& fields) - : cursor_(fields.begin()), - saved_cursor_(fields.begin()), - begin_(fields.begin()), - end_(fields.end()) { +AutofillScanner::AutofillScanner(const std::vector<AutofillField*>& fields) { + Init(fields); +} + +AutofillScanner::AutofillScanner( + const std::vector<std::unique_ptr<AutofillField>>& fields) { + for (const auto& field : fields) + non_owning_.push_back(field.get()); + + Init(non_owning_); } AutofillScanner::~AutofillScanner() { @@ -27,7 +32,7 @@ void AutofillScanner::Advance() { AutofillField* AutofillScanner::Cursor() const { if (IsEnd()) { NOTREACHED(); - return NULL; + return nullptr; } return *cursor_; @@ -54,4 +59,11 @@ size_t AutofillScanner::SaveCursor() { return static_cast<size_t>(cursor_ - begin_); } +void AutofillScanner::Init(const std::vector<AutofillField*>& fields) { + cursor_ = fields.begin(); + saved_cursor_ = fields.begin(); + begin_ = fields.begin(); + end_ = fields.end(); +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_scanner.h b/chromium/components/autofill/core/browser/autofill_scanner.h index f30d5abcbc3..88b84e6fd94 100644 --- a/chromium/components/autofill/core/browser/autofill_scanner.h +++ b/chromium/components/autofill/core/browser/autofill_scanner.h @@ -7,6 +7,7 @@ #include <stddef.h> +#include <memory> #include <vector> #include "base/macros.h" @@ -20,6 +21,8 @@ class AutofillField; class AutofillScanner { public: explicit AutofillScanner(const std::vector<AutofillField*>& fields); + explicit AutofillScanner( + const std::vector<std::unique_ptr<AutofillField>>& fields); ~AutofillScanner(); // Advances the cursor by one step, if possible. @@ -43,6 +46,8 @@ class AutofillScanner { size_t SaveCursor(); private: + void Init(const std::vector<AutofillField*>& fields); + // Indicates the current position in the stream, represented as a vector. std::vector<AutofillField*>::const_iterator cursor_; @@ -50,10 +55,13 @@ class AutofillScanner { std::vector<AutofillField*>::const_iterator saved_cursor_; // The beginning pointer for the stream. - const std::vector<AutofillField*>::const_iterator begin_; + std::vector<AutofillField*>::const_iterator begin_; // The past-the-end pointer for the stream. - const std::vector<AutofillField*>::const_iterator end_; + std::vector<AutofillField*>::const_iterator end_; + + // The storage of non-owning pointers, used for the unique_ptr constructor. + std::vector<AutofillField*> non_owning_; DISALLOW_COPY_AND_ASSIGN(AutofillScanner); }; diff --git a/chromium/components/autofill/core/browser/autofill_type.cc b/chromium/components/autofill/core/browser/autofill_type.cc index 47081994a9d..8dc9dbc85ad 100644 --- a/chromium/components/autofill/core/browser/autofill_type.cc +++ b/chromium/components/autofill/core/browser/autofill_type.cc @@ -126,6 +126,7 @@ FieldTypeGroup AutofillType::group() const { case PROBABLY_NEW_PASSWORD: case NOT_NEW_PASSWORD: case PROBABLY_ACCOUNT_CREATION_PASSWORD: + case CONFIRMATION_PASSWORD: return PASSWORD_FIELD; case NO_SERVER_DATA: @@ -757,6 +758,8 @@ std::string AutofillType::ServerFieldTypeToString(ServerFieldType type) { return "NOT_NEW_PASSWORD"; case PROBABLY_ACCOUNT_CREATION_PASSWORD: return "PROBABLY_ACCOUNT_CREATION_PASSWORD"; + case CONFIRMATION_PASSWORD: + return "CONFIRMATION_PASSWORD"; case MAX_VALID_FIELD_TYPE: return std::string(); diff --git a/chromium/components/autofill/core/browser/autofill_wallet_data_type_controller.cc b/chromium/components/autofill/core/browser/autofill_wallet_data_type_controller.cc index 61bf8140a6c..ecdcce20fa0 100644 --- a/chromium/components/autofill/core/browser/autofill_wallet_data_type_controller.cc +++ b/chromium/components/autofill/core/browser/autofill_wallet_data_type_controller.cc @@ -4,6 +4,8 @@ #include "components/autofill/core/browser/autofill_wallet_data_type_controller.h" +#include <utility> + #include "base/bind.h" #include "components/autofill/core/browser/personal_data_manager.h" #include "components/autofill/core/browser/webdata/autofill_webdata_service.h" @@ -18,13 +20,15 @@ namespace browser_sync { AutofillWalletDataTypeController::AutofillWalletDataTypeController( syncer::ModelType type, - const scoped_refptr<base::SingleThreadTaskRunner>& db_thread, + scoped_refptr<base::SingleThreadTaskRunner> db_thread, const base::Closure& dump_stack, syncer::SyncClient* sync_client, const scoped_refptr<autofill::AutofillWebDataService>& web_data_service) - : NonUIDataTypeController(type, dump_stack, sync_client), - db_thread_(db_thread), - sync_client_(sync_client), + : AsyncDirectoryTypeController(type, + dump_stack, + sync_client, + syncer::GROUP_DB, + std::move(db_thread)), callback_registered_(false), web_data_service_(web_data_service), currently_enabled_(IsEnabled()) { @@ -39,18 +43,6 @@ AutofillWalletDataTypeController::AutofillWalletDataTypeController( AutofillWalletDataTypeController::~AutofillWalletDataTypeController() {} -syncer::ModelSafeGroup AutofillWalletDataTypeController::model_safe_group() - const { - return syncer::GROUP_DB; -} - -bool AutofillWalletDataTypeController::PostTaskOnBackendThread( - const tracked_objects::Location& from_here, - const base::Closure& task) { - DCHECK(CalledOnValidThread()); - return db_thread_->PostTask(from_here, task); -} - bool AutofillWalletDataTypeController::StartModels() { DCHECK(CalledOnValidThread()); DCHECK_EQ(state(), MODEL_STARTING); diff --git a/chromium/components/autofill/core/browser/autofill_wallet_data_type_controller.h b/chromium/components/autofill/core/browser/autofill_wallet_data_type_controller.h index e483e0b304f..cffe8628f57 100644 --- a/chromium/components/autofill/core/browser/autofill_wallet_data_type_controller.h +++ b/chromium/components/autofill/core/browser/autofill_wallet_data_type_controller.h @@ -7,7 +7,7 @@ #include "base/macros.h" #include "components/prefs/pref_change_registrar.h" -#include "components/sync/driver/non_ui_data_type_controller.h" +#include "components/sync/driver/async_directory_type_controller.h" namespace autofill { class AutofillWebDataService; @@ -17,25 +17,20 @@ namespace browser_sync { // Controls syncing of either AUTOFILL_WALLET or AUTOFILL_WALLET_METADATA. class AutofillWalletDataTypeController - : public syncer::NonUIDataTypeController { + : public syncer::AsyncDirectoryTypeController { public: // |type| should be either AUTOFILL_WALLET or AUTOFILL_WALLET_METADATA. // |dump_stack| is called when an unrecoverable error occurs. AutofillWalletDataTypeController( syncer::ModelType type, - const scoped_refptr<base::SingleThreadTaskRunner>& db_thread, + scoped_refptr<base::SingleThreadTaskRunner> db_thread, const base::Closure& dump_stack, syncer::SyncClient* sync_client, const scoped_refptr<autofill::AutofillWebDataService>& web_data_service); ~AutofillWalletDataTypeController() override; - // NonUIDataTypeController implementation. - syncer::ModelSafeGroup model_safe_group() const override; - private: - // NonUIDataTypeController implementation. - bool PostTaskOnBackendThread(const tracked_objects::Location& from_here, - const base::Closure& task) override; + // AsyncDirectoryTypeController implementation. bool StartModels() override; void StopModels() override; bool ReadyForStart() const override; @@ -46,12 +41,6 @@ class AutofillWalletDataTypeController // Returns true if the prefs are set such that wallet sync should be enabled. bool IsEnabled(); - // A reference to the DB thread's task runner. - const scoped_refptr<base::SingleThreadTaskRunner> db_thread_; - - // A pointer to the sync client. - syncer::SyncClient* const sync_client_; - // Whether the database loaded callback has been registered. bool callback_registered_; diff --git a/chromium/components/autofill/core/browser/credit_card.cc b/chromium/components/autofill/core/browser/credit_card.cc index 75a34c75a26..3b402eae9e8 100644 --- a/chromium/components/autofill/core/browser/credit_card.cc +++ b/chromium/components/autofill/core/browser/credit_card.cc @@ -39,12 +39,15 @@ using base::ASCIIToUTF16; namespace autofill { -const base::char16 kMidlineEllipsis[] = { 0x22ef, 0 }; +const base::char16 kMidlineEllipsis[] = { 0x0020, 0x0020, + 0x2022, 0x2006, + 0x2022, 0x2006, + 0x2022, 0x2006, + 0x2022, 0x2006, 0 }; namespace { const base::char16 kCreditCardObfuscationSymbol = '*'; -const base::char16 kNonBreakingSpace[] = { 0x00a0, 0 }; bool ConvertYear(const base::string16& year, int* num) { // If the |year| is empty, clear the stored value. @@ -72,6 +75,8 @@ base::string16 TypeForFill(const std::string& type) { return l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_JCB); if (type == kMasterCard) return l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_MASTERCARD); + if (type == kMirCard) + return l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_MIR); if (type == kUnionPay) return l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_UNION_PAY); if (type == kVisaCard) @@ -130,13 +135,15 @@ int CreditCard::IconResourceId(const std::string& type) { if (type == kAmericanExpressCard) return IDR_AUTOFILL_CC_AMEX; if (type == kDinersCard) - return IDR_AUTOFILL_CC_GENERIC; + return IDR_AUTOFILL_CC_DINERS; if (type == kDiscoverCard) return IDR_AUTOFILL_CC_DISCOVER; if (type == kJCBCard) - return IDR_AUTOFILL_CC_GENERIC; + return IDR_AUTOFILL_CC_JCB; if (type == kMasterCard) return IDR_AUTOFILL_CC_MASTERCARD; + if (type == kMirCard) + return IDR_AUTOFILL_CC_MIR; if (type == kUnionPay) return IDR_AUTOFILL_CC_GENERIC; if (type == kVisaCard) @@ -188,6 +195,9 @@ const char* CreditCard::GetCreditCardType(const base::string16& number) { if (!base::StringToInt(number.substr(0, 2), &first_two_digits)) return kGenericCard; + if (first_two_digits == 22) + return kMirCard; + if (first_two_digits == 34 || first_two_digits == 37) return kAmericanExpressCard; @@ -492,8 +502,7 @@ base::string16 CreditCard::TypeAndLastFourDigits() const { return type; // TODO(estade): i18n? - return type + base::string16(kNonBreakingSpace) + - base::string16(kMidlineEllipsis) + digits; + return type + base::string16(kMidlineEllipsis) + digits; } base::string16 CreditCard::AbbreviatedExpirationDateForDisplay() const { @@ -865,6 +874,7 @@ const char kDiscoverCard[] = "discoverCC"; const char kGenericCard[] = "genericCC"; const char kJCBCard[] = "jcbCC"; const char kMasterCard[] = "masterCardCC"; +const char kMirCard[] = "mirCC"; const char kUnionPay[] = "unionPayCC"; const char kVisaCard[] = "visaCC"; diff --git a/chromium/components/autofill/core/browser/credit_card.h b/chromium/components/autofill/core/browser/credit_card.h index 1e4efd12f37..d0a8a6eee7e 100644 --- a/chromium/components/autofill/core/browser/credit_card.h +++ b/chromium/components/autofill/core/browser/credit_card.h @@ -266,6 +266,7 @@ extern const char kDiscoverCard[]; extern const char kGenericCard[]; extern const char kJCBCard[]; extern const char kMasterCard[]; +extern const char kMirCard[]; extern const char kUnionPay[]; extern const char kVisaCard[]; diff --git a/chromium/components/autofill/core/browser/credit_card_field_unittest.cc b/chromium/components/autofill/core/browser/credit_card_field_unittest.cc index b6cf5757216..5b44a651d9c 100644 --- a/chromium/components/autofill/core/browser/credit_card_field_unittest.cc +++ b/chromium/components/autofill/core/browser/credit_card_field_unittest.cc @@ -5,10 +5,10 @@ #include "components/autofill/core/browser/credit_card_field.h" #include <memory> +#include <vector> #include "base/macros.h" #include "base/memory/ptr_util.h" -#include "base/memory/scoped_vector.h" #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/autofill_field.h" #include "components/autofill/core/browser/autofill_scanner.h" @@ -25,14 +25,14 @@ class CreditCardFieldTest : public testing::Test { ~CreditCardFieldTest() override {} protected: - ScopedVector<AutofillField> list_; + std::vector<std::unique_ptr<AutofillField>> list_; std::unique_ptr<const CreditCardField> field_; FieldCandidatesMap field_candidates_map_; // Parses the contents of |list_| as a form, and stores the result into // |field_|. void Parse() { - AutofillScanner scanner(list_.get()); + AutofillScanner scanner(list_); std::unique_ptr<FormField> field = CreditCardField::Parse(&scanner); field_ = base::WrapUnique(static_cast<CreditCardField*>(field.release())); } @@ -40,7 +40,7 @@ class CreditCardFieldTest : public testing::Test { void MultipleParses() { std::unique_ptr<FormField> field; - AutofillScanner scanner(list_.get()); + AutofillScanner scanner(list_); while (!scanner.IsEnd()) { field = CreditCardField::Parse(&scanner); field_ = base::WrapUnique(static_cast<CreditCardField*>(field.release())); @@ -68,7 +68,7 @@ TEST_F(CreditCardFieldTest, Empty) { } TEST_F(CreditCardFieldTest, NonParse) { - list_.push_back(new AutofillField); + list_.push_back(base::MakeUnique<AutofillField>()); Parse(); ASSERT_EQ(nullptr, field_.get()); } @@ -79,11 +79,13 @@ TEST_F(CreditCardFieldTest, ParseCreditCardNoNumber) { field.label = ASCIIToUTF16("Exp Month"); field.name = ASCIIToUTF16("ccmonth"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("month1"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("month1"))); field.label = ASCIIToUTF16("Exp Year"); field.name = ASCIIToUTF16("ccyear"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("year2"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("year2"))); Parse(); ASSERT_EQ(nullptr, field_.get()); @@ -95,7 +97,8 @@ TEST_F(CreditCardFieldTest, ParseCreditCardNoDate) { field.label = ASCIIToUTF16("Card Number"); field.name = ASCIIToUTF16("card_number"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("number1"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("number1"))); Parse(); ASSERT_EQ(nullptr, field_.get()); @@ -107,15 +110,18 @@ TEST_F(CreditCardFieldTest, ParseMiniumCreditCard) { field.label = ASCIIToUTF16("Card Number"); field.name = ASCIIToUTF16("card_number"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("number1"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("number1"))); field.label = ASCIIToUTF16("Exp Month"); field.name = ASCIIToUTF16("ccmonth"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("month2"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("month2"))); field.label = ASCIIToUTF16("Exp Year"); field.name = ASCIIToUTF16("ccyear"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("year3"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("year3"))); Parse(); ASSERT_NE(nullptr, field_.get()); @@ -140,30 +146,32 @@ TEST_F(CreditCardFieldTest, ParseFullCreditCard) { field.label = ASCIIToUTF16("Name on Card"); field.name = ASCIIToUTF16("name_on_card"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("name"))); + list_.push_back(base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name"))); field.label = ASCIIToUTF16("Card Number"); field.name = ASCIIToUTF16("card_number"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("number"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("number"))); field.label = ASCIIToUTF16("Exp Month"); field.name = ASCIIToUTF16("ccmonth"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("month"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("month"))); field.label = ASCIIToUTF16("Exp Year"); field.name = ASCIIToUTF16("ccyear"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("year"))); + list_.push_back(base::MakeUnique<AutofillField>(field, ASCIIToUTF16("year"))); field.label = ASCIIToUTF16("Verification"); field.name = ASCIIToUTF16("verification"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("cvc"))); + list_.push_back(base::MakeUnique<AutofillField>(field, ASCIIToUTF16("cvc"))); field.form_control_type = "select-one"; field.label = ASCIIToUTF16("Card Type"); field.name = ASCIIToUTF16("card_type"); field.option_contents.push_back(ASCIIToUTF16("visa")); field.option_values.push_back(ASCIIToUTF16("visa")); - list_.push_back(new AutofillField(field, ASCIIToUTF16("type"))); + list_.push_back(base::MakeUnique<AutofillField>(field, ASCIIToUTF16("type"))); Parse(); ASSERT_NE(nullptr, field_.get()); @@ -200,19 +208,23 @@ TEST_F(CreditCardFieldTest, ParseExpMonthYear) { field.label = ASCIIToUTF16("Name on Card"); field.name = ASCIIToUTF16("name_on_card"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("name1"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name1"))); field.label = ASCIIToUTF16("Card Number"); field.name = ASCIIToUTF16("card_number"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("number2"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("number2"))); field.label = ASCIIToUTF16("ExpDate Month / Year"); field.name = ASCIIToUTF16("ExpDate"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("month3"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("month3"))); field.label = ASCIIToUTF16("ExpDate Month / Year"); field.name = ASCIIToUTF16("ExpDate"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("year4"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("year4"))); Parse(); ASSERT_NE(nullptr, field_.get()); @@ -241,19 +253,23 @@ TEST_F(CreditCardFieldTest, ParseExpMonthYear2) { field.label = ASCIIToUTF16("Name on Card"); field.name = ASCIIToUTF16("name_on_card"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("name1"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name1"))); field.label = ASCIIToUTF16("Card Number"); field.name = ASCIIToUTF16("card_number"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("number2"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("number2"))); field.label = ASCIIToUTF16("Expiration date Month / Year"); field.name = ASCIIToUTF16("ExpDate"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("month3"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("month3"))); field.label = ASCIIToUTF16("Expiration date Month / Year"); field.name = ASCIIToUTF16("ExpDate"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("year4"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("year4"))); Parse(); ASSERT_NE(nullptr, field_.get()); @@ -343,18 +359,21 @@ TEST_F(CreditCardFieldTest, ParseExpField) { field.label = ASCIIToUTF16("Name on Card"); field.name = ASCIIToUTF16("name_on_card"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("name1"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name1"))); field.label = ASCIIToUTF16("Card Number"); field.name = ASCIIToUTF16("card_number"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("num2"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("num2"))); field.label = ASCIIToUTF16(test_case.label); if (test_case.max_length != 0) { field.max_length = test_case.max_length; } field.name = ASCIIToUTF16("cc_exp"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("exp3"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("exp3"))); Parse(); @@ -397,7 +416,8 @@ TEST_F(CreditCardFieldTest, ParseCreditCardHolderNameWithCCFullName) { field.label = ASCIIToUTF16("Name"); field.name = ASCIIToUTF16("ccfullname"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("name1"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name1"))); Parse(); ASSERT_NE(nullptr, field_.get()); @@ -415,12 +435,14 @@ TEST_F(CreditCardFieldTest, ParseMonthControl) { field.form_control_type = "text"; field.label = ASCIIToUTF16("Card number:"); field.name = ASCIIToUTF16("ccnumber"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("number1"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("number1"))); field.form_control_type = "month"; field.label = ASCIIToUTF16("Expiration date:"); field.name = ASCIIToUTF16("ccexp"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("date2"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("date2"))); Parse(); ASSERT_NE(nullptr, field_.get()); @@ -443,15 +465,17 @@ TEST_F(CreditCardFieldTest, ParseCreditCardExpYear_2DigitMaxLength) { field.label = ASCIIToUTF16("Card Number"); field.name = ASCIIToUTF16("card_number"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("number"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("number"))); field.label = ASCIIToUTF16("Expiration Date"); field.name = ASCIIToUTF16("ccmonth"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("month"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("month"))); field.name = ASCIIToUTF16("ccyear"); field.max_length = 2; - list_.push_back(new AutofillField(field, ASCIIToUTF16("year"))); + list_.push_back(base::MakeUnique<AutofillField>(field, ASCIIToUTF16("year"))); Parse(); ASSERT_NE(nullptr, field_.get()); @@ -477,17 +501,20 @@ TEST_F(CreditCardFieldTest, ParseCreditCardNumberWithSplit) { field.label = ASCIIToUTF16("Card Number"); field.name = ASCIIToUTF16("card_number_q1"); field.max_length = 4; - list_.push_back(new AutofillField(field, ASCIIToUTF16("number1"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("number1"))); field.label = ASCIIToUTF16("Card Number"); field.name = ASCIIToUTF16("card_number_q2"); field.max_length = 4; - list_.push_back(new AutofillField(field, ASCIIToUTF16("number2"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("number2"))); field.label = ASCIIToUTF16("Card Number"); field.name = ASCIIToUTF16("card_number_q3"); field.max_length = 4; - list_.push_back(new AutofillField(field, ASCIIToUTF16("number3"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("number3"))); // For last credit card number input field it simply ignores the |max_length| // attribute. So even having a very big number, does not conside it an invalid @@ -495,15 +522,18 @@ TEST_F(CreditCardFieldTest, ParseCreditCardNumberWithSplit) { field.label = ASCIIToUTF16("Card Number"); field.name = ASCIIToUTF16("card_number_q4"); field.max_length = 20; - list_.push_back(new AutofillField(field, ASCIIToUTF16("number4"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("number4"))); field.label = ASCIIToUTF16("Exp Month"); field.name = ASCIIToUTF16("ccmonth"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("month5"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("month5"))); field.label = ASCIIToUTF16("Exp Year"); field.name = ASCIIToUTF16("ccyear"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("year6"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("year6"))); Parse(); ASSERT_NE(nullptr, field_.get()); @@ -549,23 +579,28 @@ TEST_F(CreditCardFieldTest, ParseMultipleCreditCardNumbers) { field.label = ASCIIToUTF16("Name on Card"); field.name = ASCIIToUTF16("name_on_card"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("name1"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name1"))); field.label = ASCIIToUTF16("Card Number"); field.name = ASCIIToUTF16("card_number"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("number2"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("number2"))); field.label = ASCIIToUTF16("Confirm Card Number"); field.name = ASCIIToUTF16("confirm_card_number"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("number3"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("number3"))); field.label = ASCIIToUTF16("Exp Month"); field.name = ASCIIToUTF16("ccmonth"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("month4"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("month4"))); field.label = ASCIIToUTF16("Exp Year"); field.name = ASCIIToUTF16("ccyear"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("year5"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("year5"))); Parse(); ASSERT_NE(nullptr, field_.get()); @@ -599,23 +634,28 @@ TEST_F(CreditCardFieldTest, ParseFirstAndLastNames) { field.label = ASCIIToUTF16("First Name on Card"); field.name = ASCIIToUTF16("cc-fname"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("name1"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name1"))); field.label = ASCIIToUTF16("Last Name"); field.name = ASCIIToUTF16("cc-lname"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("name2"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name2"))); field.label = ASCIIToUTF16("Card Number"); field.name = ASCIIToUTF16("card_number"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("number3"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("number3"))); field.label = ASCIIToUTF16("Exp Month"); field.name = ASCIIToUTF16("ccmonth"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("month4"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("month4"))); field.label = ASCIIToUTF16("Exp Year"); field.name = ASCIIToUTF16("ccyear"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("year5"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("year5"))); Parse(); ASSERT_NE(nullptr, field_.get()); @@ -649,27 +689,29 @@ TEST_F(CreditCardFieldTest, ParseConsecutiveCvc) { field.label = ASCIIToUTF16("Name on Card"); field.name = ASCIIToUTF16("name_on_card"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("name"))); + list_.push_back(base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name"))); field.label = ASCIIToUTF16("Card Number"); field.name = ASCIIToUTF16("card_number"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("number"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("number"))); field.label = ASCIIToUTF16("Exp Month"); field.name = ASCIIToUTF16("ccmonth"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("month"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("month"))); field.label = ASCIIToUTF16("Exp Year"); field.name = ASCIIToUTF16("ccyear"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("year"))); + list_.push_back(base::MakeUnique<AutofillField>(field, ASCIIToUTF16("year"))); field.label = ASCIIToUTF16("Verification"); field.name = ASCIIToUTF16("verification"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("cvc"))); + list_.push_back(base::MakeUnique<AutofillField>(field, ASCIIToUTF16("cvc"))); field.label = ASCIIToUTF16("Verification"); field.name = ASCIIToUTF16("verification"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("cvc2"))); + list_.push_back(base::MakeUnique<AutofillField>(field, ASCIIToUTF16("cvc2"))); MultipleParses(); @@ -705,31 +747,34 @@ TEST_F(CreditCardFieldTest, ParseNonConsecutiveCvc) { field.label = ASCIIToUTF16("Name on Card"); field.name = ASCIIToUTF16("name_on_card"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("name"))); + list_.push_back(base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name"))); field.label = ASCIIToUTF16("Card Number"); field.name = ASCIIToUTF16("card_number"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("number"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("number"))); field.label = ASCIIToUTF16("Exp Month"); field.name = ASCIIToUTF16("ccmonth"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("month"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("month"))); field.label = ASCIIToUTF16("Exp Year"); field.name = ASCIIToUTF16("ccyear"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("year"))); + list_.push_back(base::MakeUnique<AutofillField>(field, ASCIIToUTF16("year"))); field.label = ASCIIToUTF16("Verification"); field.name = ASCIIToUTF16("verification"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("cvc"))); + list_.push_back(base::MakeUnique<AutofillField>(field, ASCIIToUTF16("cvc"))); field.label = ASCIIToUTF16("Unknown"); field.name = ASCIIToUTF16("unknown"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("unknown"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("unknown"))); field.label = ASCIIToUTF16("Verification"); field.name = ASCIIToUTF16("verification"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("cvc2"))); + list_.push_back(base::MakeUnique<AutofillField>(field, ASCIIToUTF16("cvc2"))); MultipleParses(); diff --git a/chromium/components/autofill/core/browser/credit_card_unittest.cc b/chromium/components/autofill/core/browser/credit_card_unittest.cc index 3f481ee956d..de26d8e56d3 100644 --- a/chromium/components/autofill/core/browser/credit_card_unittest.cc +++ b/chromium/components/autofill/core/browser/credit_card_unittest.cc @@ -59,6 +59,13 @@ const char* const kInvalidNumbers[] = { "3056 9309 0259 04aa", /* non-digit characters */ }; +const std::string kUTF8MidlineEllipsis = + " " + "\xE2\x80\xA2\xE2\x80\x86" + "\xE2\x80\xA2\xE2\x80\x86" + "\xE2\x80\xA2\xE2\x80\x86" + "\xE2\x80\xA2\xE2\x80\x86"; + } // namespace // Tests credit card summary string generation. This test simulates a variety @@ -93,14 +100,10 @@ TEST(CreditCardTest, PreviewSummaryAndTypeAndLastFourDigitsStrings) { test::SetCreditCardInfo( &credit_card2, "John Dillinger", "5105 1051 0510 5100", "", "2010"); base::string16 summary2 = credit_card2.Label(); - EXPECT_EQ(UTF8ToUTF16( - "MasterCard\xC2\xA0\xE2\x8B\xAF" - "5100"), + EXPECT_EQ(UTF8ToUTF16("MasterCard" + kUTF8MidlineEllipsis + "5100"), summary2); base::string16 obfuscated2 = credit_card2.TypeAndLastFourDigits(); - EXPECT_EQ(UTF8ToUTF16( - "MasterCard\xC2\xA0\xE2\x8B\xAF" - "5100"), + EXPECT_EQ(UTF8ToUTF16("MasterCard" + kUTF8MidlineEllipsis + "5100"), obfuscated2); // Case 3: No year. @@ -108,14 +111,10 @@ TEST(CreditCardTest, PreviewSummaryAndTypeAndLastFourDigitsStrings) { test::SetCreditCardInfo( &credit_card3, "John Dillinger", "5105 1051 0510 5100", "01", ""); base::string16 summary3 = credit_card3.Label(); - EXPECT_EQ(UTF8ToUTF16( - "MasterCard\xC2\xA0\xE2\x8B\xAF" - "5100"), + EXPECT_EQ(UTF8ToUTF16("MasterCard" + kUTF8MidlineEllipsis + "5100"), summary3); base::string16 obfuscated3 = credit_card3.TypeAndLastFourDigits(); - EXPECT_EQ(UTF8ToUTF16( - "MasterCard\xC2\xA0\xE2\x8B\xAF" - "5100"), + EXPECT_EQ(UTF8ToUTF16("MasterCard" + kUTF8MidlineEllipsis + "5100"), obfuscated3); // Case 4: Have everything. @@ -123,14 +122,10 @@ TEST(CreditCardTest, PreviewSummaryAndTypeAndLastFourDigitsStrings) { test::SetCreditCardInfo( &credit_card4, "John Dillinger", "5105 1051 0510 5100", "01", "2010"); base::string16 summary4 = credit_card4.Label(); - EXPECT_EQ(UTF8ToUTF16( - "MasterCard\xC2\xA0\xE2\x8B\xAF" - "5100, 01/2010"), + EXPECT_EQ(UTF8ToUTF16("MasterCard" + kUTF8MidlineEllipsis + "5100, 01/2010"), summary4); base::string16 obfuscated4 = credit_card4.TypeAndLastFourDigits(); - EXPECT_EQ(UTF8ToUTF16( - "MasterCard\xC2\xA0\xE2\x8B\xAF" - "5100"), + EXPECT_EQ(UTF8ToUTF16("MasterCard" + kUTF8MidlineEllipsis + "5100"), obfuscated4); // Case 5: Very long credit card @@ -140,14 +135,10 @@ TEST(CreditCardTest, PreviewSummaryAndTypeAndLastFourDigitsStrings) { "John Dillinger", "0123456789 0123456789 0123456789 5105 1051 0510 5100", "01", "2010"); base::string16 summary5 = credit_card5.Label(); - EXPECT_EQ(UTF8ToUTF16( - "Card\xC2\xA0\xE2\x8B\xAF" - "5100, 01/2010"), + EXPECT_EQ(UTF8ToUTF16("Card" + kUTF8MidlineEllipsis + "5100, 01/2010"), summary5); base::string16 obfuscated5 = credit_card5.TypeAndLastFourDigits(); - EXPECT_EQ(UTF8ToUTF16( - "Card\xC2\xA0\xE2\x8B\xAF" - "5100"), + EXPECT_EQ(UTF8ToUTF16("Card" + kUTF8MidlineEllipsis + "5100"), obfuscated5); } @@ -378,14 +369,16 @@ TEST(CreditCardTest, Compare) { TEST(CreditCardTest, IconResourceId) { EXPECT_EQ(IDR_AUTOFILL_CC_AMEX, CreditCard::IconResourceId(kAmericanExpressCard)); - EXPECT_EQ(IDR_AUTOFILL_CC_GENERIC, + EXPECT_EQ(IDR_AUTOFILL_CC_DINERS, CreditCard::IconResourceId(kDinersCard)); EXPECT_EQ(IDR_AUTOFILL_CC_DISCOVER, CreditCard::IconResourceId(kDiscoverCard)); - EXPECT_EQ(IDR_AUTOFILL_CC_GENERIC, + EXPECT_EQ(IDR_AUTOFILL_CC_JCB, CreditCard::IconResourceId(kJCBCard)); EXPECT_EQ(IDR_AUTOFILL_CC_MASTERCARD, CreditCard::IconResourceId(kMasterCard)); + EXPECT_EQ(IDR_AUTOFILL_CC_MIR, + CreditCard::IconResourceId(kMirCard)); EXPECT_EQ(IDR_AUTOFILL_CC_VISA, CreditCard::IconResourceId(kVisaCard)); } @@ -659,6 +652,9 @@ TEST(CreditCardTest, GetCreditCardType) { { "6247130048162403", kUnionPay, true }, { "6247130048162403", kUnionPay, true }, { "622384452162063648", kUnionPay, true }, + { "2204883716636153", kMirCard, true }, + { "2200111234567898", kMirCard, true }, + { "2200481349288130", kMirCard, true }, // Empty string { std::string(), kGenericCard, false }, @@ -670,13 +666,16 @@ TEST(CreditCardTest, GetCreditCardType) { // Fails Luhn check. { "4111111111111112", kVisaCard, false }, { "6247130048162413", kUnionPay, false }, + { "2204883716636154", kMirCard, false }, // Invalid length. { "3434343434343434", kAmericanExpressCard, false }, { "411111111111116", kVisaCard, false }, + { "220011123456783", kMirCard, false }, // Issuer Identification Numbers (IINs) that Chrome recognizes. { "4", kVisaCard, false }, + { "22", kMirCard, false }, { "34", kAmericanExpressCard, false }, { "37", kAmericanExpressCard, false }, { "300", kDinersCard, false }, @@ -708,6 +707,7 @@ TEST(CreditCardTest, GetCreditCardType) { { "62", kUnionPay, false }, // Not enough data to determine an IIN uniquely. + { "2", kGenericCard, false }, { "3", kGenericCard, false }, { "30", kGenericCard, false }, { "309", kGenericCard, false }, @@ -721,7 +721,6 @@ TEST(CreditCardTest, GetCreditCardType) { // Unknown IINs. { "0", kGenericCard, false }, { "1", kGenericCard, false }, - { "2", kGenericCard, false }, { "306", kGenericCard, false }, { "307", kGenericCard, false }, { "308", kGenericCard, false }, diff --git a/chromium/components/autofill/core/browser/field_types.h b/chromium/components/autofill/core/browser/field_types.h index 7dee4279e2e..6c2aa807752 100644 --- a/chromium/components/autofill/core/browser/field_types.h +++ b/chromium/components/autofill/core/browser/field_types.h @@ -159,9 +159,13 @@ enum ServerFieldType { // for local heuristics. PROBABLY_ACCOUNT_CREATION_PASSWORD = 94, + // The confirmation password field in account creation or change password + // forms. + CONFIRMATION_PASSWORD = 95, + // No new types can be added without a corresponding change to the Autofill // server. - MAX_VALID_FIELD_TYPE = 95, + MAX_VALID_FIELD_TYPE = 96, }; // The list of all HTML autocomplete field type hints supported by Chrome. diff --git a/chromium/components/autofill/core/browser/form_field.cc b/chromium/components/autofill/core/browser/form_field.cc index 3d31c0bd59f..a1997ebbb36 100644 --- a/chromium/components/autofill/core/browser/form_field.cc +++ b/chromium/components/autofill/core/browser/form_field.cc @@ -27,19 +27,6 @@ #include "components/autofill/core/common/autofill_util.h" namespace autofill { -namespace { - -bool ShouldBeProcessed(const AutofillField* field) { - // Ignore checkable fields as they interfere with parsers assuming context. - // Eg., while parsing address, "Is PO box" checkbox after ADDRESS_LINE1 - // interferes with correctly understanding ADDRESS_LINE2. - // Ignore fields marked as presentational. See - // http://www.w3.org/TR/wai-aria/roles#presentation - return !(IsCheckable(field->check_status) || - field->role == FormFieldData::ROLE_ATTRIBUTE_PRESENTATION); -} - -} // namespace // There's an implicit precedence determined by the values assigned here. Email // is currently the most important followed by Phone, Address, Credit Card and @@ -52,12 +39,22 @@ const float FormField::kBaseNameParserScore = 1.0f; // static FieldCandidatesMap FormField::ParseFormFields( - const std::vector<AutofillField*>& fields, + const std::vector<std::unique_ptr<AutofillField>>& fields, bool is_form_tag) { // Set up a working copy of the fields to be processed. std::vector<AutofillField*> processed_fields; - std::copy_if(fields.begin(), fields.end(), - std::back_inserter(processed_fields), ShouldBeProcessed); + for (const auto& field : fields) { + // Ignore checkable fields as they interfere with parsers assuming context. + // Eg., while parsing address, "Is PO box" checkbox after ADDRESS_LINE1 + // interferes with correctly understanding ADDRESS_LINE2. + // Ignore fields marked as presentational. See + // http://www.w3.org/TR/wai-aria/roles#presentation + if (IsCheckable(field->check_status) || + field->role == FormFieldData::ROLE_ATTRIBUTE_PRESENTATION) { + continue; + } + processed_fields.push_back(field.get()); + } FieldCandidatesMap field_candidates; @@ -175,7 +172,7 @@ void FormField::ParseFormFieldsPass(ParseFunction parse, FieldCandidatesMap* field_candidates) { AutofillScanner scanner(fields); while (!scanner.IsEnd()) { - std::unique_ptr<FormField> form_field(parse(&scanner)); + std::unique_ptr<FormField> form_field = parse(&scanner); if (form_field == nullptr) { scanner.Advance(); } else { diff --git a/chromium/components/autofill/core/browser/form_field.h b/chromium/components/autofill/core/browser/form_field.h index 2d7dcd7dbf4..3ec73b4cf3a 100644 --- a/chromium/components/autofill/core/browser/form_field.h +++ b/chromium/components/autofill/core/browser/form_field.h @@ -30,7 +30,7 @@ class FormField { // Each field has a derived unique name that is used as the key into the // returned FieldCandidatesMap. static FieldCandidatesMap ParseFormFields( - const std::vector<AutofillField*>& fields, + const std::vector<std::unique_ptr<AutofillField>>& fields, bool is_form_tag); protected: diff --git a/chromium/components/autofill/core/browser/form_field_unittest.cc b/chromium/components/autofill/core/browser/form_field_unittest.cc index 71e5e12d476..8962409833c 100644 --- a/chromium/components/autofill/core/browser/form_field_unittest.cc +++ b/chromium/components/autofill/core/browser/form_field_unittest.cc @@ -2,7 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/memory/scoped_vector.h" +#include <memory> +#include <vector> + +#include "base/memory/ptr_util.h" #include "base/strings/string16.h" #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/autofill_field.h" @@ -124,32 +127,36 @@ TEST(FormFieldTest, Match) { // Test that we ignore checkable elements. TEST(FormFieldTest, ParseFormFields) { - ScopedVector<AutofillField> fields; + std::vector<std::unique_ptr<AutofillField>> fields; FormFieldData field_data; field_data.form_control_type = "text"; field_data.label = ASCIIToUTF16("Address line1"); - fields.push_back(new AutofillField(field_data, field_data.label)); + fields.push_back( + base::MakeUnique<AutofillField>(field_data, field_data.label)); field_data.check_status = FormFieldData::CHECKABLE_BUT_UNCHECKED; field_data.label = ASCIIToUTF16("Is PO Box"); - fields.push_back(new AutofillField(field_data, field_data.label)); + fields.push_back( + base::MakeUnique<AutofillField>(field_data, field_data.label)); // reset |is_checkable| to false. field_data.check_status = FormFieldData::NOT_CHECKABLE; field_data.label = ASCIIToUTF16("Address line2"); - fields.push_back(new AutofillField(field_data, field_data.label)); + fields.push_back( + base::MakeUnique<AutofillField>(field_data, field_data.label)); // Does not parse since there are only 2 recognized fields. - ASSERT_TRUE(FormField::ParseFormFields(fields.get(), true).empty()); + ASSERT_TRUE(FormField::ParseFormFields(fields, true).empty()); field_data.label = ASCIIToUTF16("City"); - fields.push_back(new AutofillField(field_data, field_data.label)); + fields.push_back( + base::MakeUnique<AutofillField>(field_data, field_data.label)); // Checkable element shouldn't interfere with inference of Address line2. const FieldCandidatesMap field_candidates_map = - FormField::ParseFormFields(fields.get(), true); + FormField::ParseFormFields(fields, true); ASSERT_EQ(3U, field_candidates_map.size()); EXPECT_EQ(ADDRESS_HOME_LINE1, @@ -170,11 +177,11 @@ TEST(FormFieldTest, ParseFormFieldsImmutableForm) { field_data.form_control_type = "text"; field_data.name = ASCIIToUTF16("business_email_address"); - ScopedVector<AutofillField> fields; - fields.push_back(new AutofillField(field_data, unique_name)); + std::vector<std::unique_ptr<AutofillField>> fields; + fields.push_back(base::MakeUnique<AutofillField>(field_data, unique_name)); const FieldCandidatesMap field_candidates_map = - FormField::ParseFormFields(fields.get(), true); + FormField::ParseFormFields(fields, true); // The input form should not be modified. EXPECT_EQ(1U, fields.size()); diff --git a/chromium/components/autofill/core/browser/form_structure.cc b/chromium/components/autofill/core/browser/form_structure.cc index cf4ae5c7b15..278cc7b6de3 100644 --- a/chromium/components/autofill/core/browser/form_structure.cc +++ b/chromium/components/autofill/core/browser/form_structure.cc @@ -13,6 +13,7 @@ #include "base/command_line.h" #include "base/i18n/case_conversion.h" #include "base/logging.h" +#include "base/memory/ptr_util.h" #include "base/metrics/field_trial.h" #include "base/sha1.h" #include "base/strings/string_number_conversions.h" @@ -33,8 +34,8 @@ #include "components/autofill/core/common/form_field_data.h" #include "components/autofill/core/common/form_field_data_predictions.h" #include "components/autofill/core/common/signatures_util.h" -#include "components/rappor/rappor_service.h" -#include "components/rappor/rappor_utils.h" +#include "components/rappor/public/rappor_utils.h" +#include "components/rappor/rappor_service_impl.h" namespace autofill { namespace { @@ -321,7 +322,7 @@ FormStructure::FormStructure(const FormData& form) base::string16 unique_name = field.name + base::ASCIIToUTF16("_") + base::SizeTToString16(++unique_names[field.name]); - fields_.push_back(new AutofillField(field, unique_name)); + fields_.push_back(base::MakeUnique<AutofillField>(field, unique_name)); } form_signature_ = autofill::CalculateFormSignature(form); @@ -345,8 +346,8 @@ void FormStructure::DetermineHeuristicTypes() { if (active_field_count() >= kRequiredFieldsForPredictionRoutines && (is_form_tag_ || is_formless_checkout_)) { const FieldCandidatesMap field_type_map = - FormField::ParseFormFields(fields_.get(), is_form_tag_); - for (AutofillField* field : fields_) { + FormField::ParseFormFields(fields_, is_form_tag_); + for (const auto& field : fields_) { const auto iter = field_type_map.find(field->unique_name()); if (iter != field_type_map.end()) field->set_heuristic_type(iter->second.BestHeuristicType()); @@ -379,7 +380,7 @@ bool FormStructure::EncodeUploadRequest( // Verify that |available_field_types| agrees with the possible field types we // are uploading. - for (const AutofillField* field : *this) { + for (const auto& field : *this) { for (const auto& type : field->possible_types()) { DCHECK(type == UNKNOWN_TYPE || type == EMPTY_TYPE || available_field_types.count(type)); @@ -442,9 +443,10 @@ bool FormStructure::EncodeQueryRequest( } // static -void FormStructure::ParseQueryResponse(std::string payload, - const std::vector<FormStructure*>& forms, - rappor::RapporService* rappor_service) { +void FormStructure::ParseQueryResponse( + std::string payload, + const std::vector<FormStructure*>& forms, + rappor::RapporServiceImpl* rappor_service) { AutofillMetrics::LogServerQueryMetric( AutofillMetrics::QUERY_RESPONSE_RECEIVED); @@ -467,7 +469,7 @@ void FormStructure::ParseQueryResponse(std::string payload, response.upload_required() ? UPLOAD_REQUIRED : UPLOAD_NOT_REQUIRED; bool query_response_has_no_server_data = true; - for (AutofillField* field : form->fields_) { + for (auto& field : form->fields_) { if (form->ShouldSkipField(*field)) continue; @@ -532,7 +534,7 @@ std::vector<FormDataPredictions> FormStructure::GetFieldTypePredictions( form.data.is_form_tag = form_structure->is_form_tag_; form.signature = form_structure->FormSignatureAsStr(); - for (const AutofillField* field : form_structure->fields_) { + for (const auto& field : form_structure->fields_) { form.data.fields.push_back(FormFieldData(*field)); FormFieldDataPredictions annotated_field; @@ -573,7 +575,7 @@ bool FormStructure::IsAutofillable() const { bool FormStructure::IsCompleteCreditCardForm() const { bool found_cc_number = false; bool found_cc_expiration = false; - for (const AutofillField* field : fields_) { + for (const auto& field : fields_) { ServerFieldType type = field->Type().GetStorableType(); if (!found_cc_expiration && IsCreditCardExpirationType(type)) { found_cc_expiration = true; @@ -588,7 +590,7 @@ bool FormStructure::IsCompleteCreditCardForm() const { void FormStructure::UpdateAutofillCount() { autofill_count_ = 0; - for (const AutofillField* field : *this) { + for (const auto& field : *this) { if (field && field->IsFieldFillable()) ++autofill_count_; } @@ -609,7 +611,7 @@ bool FormStructure::ShouldBeParsed() const { return false; bool has_text_field = false; - for (const AutofillField* it : *this) { + for (const auto& it : *this) { has_text_field |= it->form_control_type != "select-one"; } @@ -626,11 +628,11 @@ void FormStructure::UpdateFromCache(const FormStructure& cached_form) { // Map from field signatures to cached fields. std::map<std::string, const AutofillField*> cached_fields; for (size_t i = 0; i < cached_form.field_count(); ++i) { - const AutofillField* field = cached_form.field(i); + const auto& field = cached_form.field(i); cached_fields[field->FieldSignatureAsStr()] = field; } - for (AutofillField* field : *this) { + for (auto& field : *this) { std::map<std::string, const AutofillField*>::const_iterator cached_field = cached_fields.find(field->FieldSignatureAsStr()); if (cached_field != cached_fields.end()) { @@ -669,7 +671,7 @@ void FormStructure::UpdateFromCache(const FormStructure& cached_form) { void FormStructure::LogQualityMetrics(const base::TimeTicks& load_time, const base::TimeTicks& interaction_time, const base::TimeTicks& submission_time, - rappor::RapporService* rappor_service, + rappor::RapporServiceImpl* rappor_service, bool did_show_suggestions, bool observed_submission) const { size_t num_detected_field_types = 0; @@ -679,7 +681,7 @@ void FormStructure::LogQualityMetrics(const base::TimeTicks& load_time, bool did_autofill_all_possible_fields = true; bool did_autofill_some_possible_fields = false; for (size_t i = 0; i < field_count(); ++i) { - const AutofillField* field = this->field(i); + const auto& field = this->field(i); // No further logging for password fields. Those are primarily related to a // different feature code path, and so make more sense to track outside of @@ -845,7 +847,7 @@ void FormStructure::LogQualityMetrics(const base::TimeTicks& load_time, } void FormStructure::LogQualityMetricsBasedOnAutocomplete() const { - for (const AutofillField* field : fields_) { + for (const auto& field : fields_) { if (field->html_type() != HTML_TYPE_UNSPECIFIED && field->html_type() != HTML_TYPE_UNRECOGNIZED) { // The type inferred by the autocomplete attribute. @@ -886,7 +888,7 @@ void FormStructure::ParseFieldTypesFromAutocompleteAttributes() { has_author_specified_types_ = false; has_author_specified_sections_ = false; - for (AutofillField* field : fields_) { + for (const auto& field : fields_) { // To prevent potential section name collisions, add a default suffix for // other fields. Without this, 'autocomplete' attribute values // "section--shipping street-address" and "shipping street-address" would be @@ -1006,7 +1008,7 @@ bool FormStructure::FillFields( std::set<base::string16> FormStructure::PossibleValues(ServerFieldType type) { std::set<base::string16> values; AutofillType target_type(type); - for (const AutofillField* field : fields_) { + for (const auto& field : fields_) { if (field->Type().GetStorableType() != target_type.GetStorableType() || field->Type().group() != target_type.group()) { continue; @@ -1034,7 +1036,7 @@ std::set<base::string16> FormStructure::PossibleValues(ServerFieldType type) { base::string16 FormStructure::GetUniqueValue(HtmlFieldType type) const { base::string16 value; - for (const AutofillField* field : fields_) { + for (const auto& field : fields_) { if (field->html_type() != type) continue; @@ -1056,7 +1058,7 @@ const AutofillField* FormStructure::field(size_t index) const { return NULL; } - return fields_[index]; + return fields_[index].get(); } AutofillField* FormStructure::field(size_t index) { @@ -1107,7 +1109,7 @@ void FormStructure::EncodeFormForQuery( DCHECK(!IsMalformed()); query_form->set_signature(form_signature()); - for (const AutofillField* field : fields_) { + for (const auto& field : fields_) { if (ShouldSkipField(*field)) continue; @@ -1128,7 +1130,7 @@ void FormStructure::EncodeFormForQuery( void FormStructure::EncodeFormForUpload(AutofillUploadContents* upload) const { DCHECK(!IsMalformed()); - for (const AutofillField* field : fields_) { + for (const auto& field : fields_) { // Don't upload checkable fields. if (IsCheckable(field->check_status)) continue; @@ -1196,7 +1198,7 @@ void FormStructure::IdentifySections(bool has_author_specified_sections) { std::set<ServerFieldType> seen_types; ServerFieldType previous_type = UNKNOWN_TYPE; - for (AutofillField* field : fields_) { + for (const auto& field : fields_) { const ServerFieldType current_type = field->Type().GetStorableType(); bool already_saw_current_type = seen_types.count(current_type) > 0; @@ -1250,7 +1252,7 @@ void FormStructure::IdentifySections(bool has_author_specified_sections) { // Ensure that credit card and address fields are in separate sections. // This simplifies the section-aware logic in autofill_manager.cc. - for (AutofillField* field : fields_) { + for (const auto& field : fields_) { FieldTypeGroup field_type_group = field->Type().group(); if (field_type_group == CREDIT_CARD) field->set_section(field->section() + "-cc"); @@ -1272,7 +1274,7 @@ void FormStructure::ProcessExtractedFields() { // Find the longest common prefix within all the field names. std::vector<base::string16> names; names.reserve(field_count()); - for (const AutofillField* field : *this) + for (const auto& field : *this) names.push_back(field->name); const base::string16 longest_prefix = FindLongestCommonPrefix(names); @@ -1280,7 +1282,7 @@ void FormStructure::ProcessExtractedFields() { return; // The name without the prefix will be used for heuristics parsing. - for (AutofillField* field : *this) { + for (auto& field : *this) { if (field->name.size() > longest_prefix.size()) { field->set_parseable_name( field->name.substr(longest_prefix.size(), field->name.size())); diff --git a/chromium/components/autofill/core/browser/form_structure.h b/chromium/components/autofill/core/browser/form_structure.h index e359a8f1f93..09c4ee13b89 100644 --- a/chromium/components/autofill/core/browser/form_structure.h +++ b/chromium/components/autofill/core/browser/form_structure.h @@ -7,6 +7,7 @@ #include <stddef.h> +#include <memory> #include <set> #include <string> #include <vector> @@ -14,7 +15,6 @@ #include "base/callback.h" #include "base/gtest_prod_util.h" #include "base/macros.h" -#include "base/memory/scoped_vector.h" #include "base/strings/string16.h" #include "base/strings/string_piece.h" #include "components/autofill/core/browser/autofill_field.h" @@ -23,8 +23,6 @@ #include "components/autofill/core/browser/proto/server.pb.h" #include "url/gurl.h" -class XmlWriter; - enum UploadRequired { UPLOAD_NOT_REQUIRED, UPLOAD_REQUIRED, @@ -35,12 +33,8 @@ namespace base { class TimeTicks; } -namespace buzz { -class XmlElement; -} - namespace rappor { -class RapporService; +class RapporServiceImpl; } namespace autofill { @@ -81,7 +75,7 @@ class FormStructure { // |rappor_service| may be null. static void ParseQueryResponse(std::string response, const std::vector<FormStructure*>& forms, - rappor::RapporService* rappor_service); + rappor::RapporServiceImpl* rappor_service); // Returns predictions using the details from the given |form_structures| and // their fields' predicted types. @@ -135,7 +129,7 @@ class FormStructure { void LogQualityMetrics(const base::TimeTicks& load_time, const base::TimeTicks& interaction_time, const base::TimeTicks& submission_time, - rappor::RapporService* rappor_service, + rappor::RapporServiceImpl* rappor_service, bool did_show_suggestions, bool observed_submission) const; @@ -194,10 +188,10 @@ class FormStructure { size_t autofill_count() const { return autofill_count_; } // Used for iterating over the fields. - std::vector<AutofillField*>::const_iterator begin() const { + std::vector<std::unique_ptr<AutofillField>>::const_iterator begin() const { return fields_.begin(); } - std::vector<AutofillField*>::const_iterator end() const { + std::vector<std::unique_ptr<AutofillField>>::const_iterator end() const { return fields_.end(); } @@ -280,7 +274,7 @@ class FormStructure { size_t autofill_count_; // A vector of all the input fields in the form. - ScopedVector<AutofillField> fields_; + 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. diff --git a/chromium/components/autofill/core/browser/legal_message_line.h b/chromium/components/autofill/core/browser/legal_message_line.h index 681173c9d58..50955f9569c 100644 --- a/chromium/components/autofill/core/browser/legal_message_line.h +++ b/chromium/components/autofill/core/browser/legal_message_line.h @@ -63,9 +63,7 @@ class LegalMessageLine { // expand correctly. // 3. "${" anywhere in the template string is invalid. // 4. "\n" embedded anywhere in the template string, or an empty template - // string, can be used to separate paragraphs. It is not possible to create - // a completely blank line by using two consecutive newlines (they will be - // treated as a single newline by views::StyledLabel). + // string, can be used to separate paragraphs. static bool Parse(const base::DictionaryValue& legal_message, LegalMessageLines* out); diff --git a/chromium/components/autofill/core/browser/name_field_unittest.cc b/chromium/components/autofill/core/browser/name_field_unittest.cc index a76c15f62b6..584becf107c 100644 --- a/chromium/components/autofill/core/browser/name_field_unittest.cc +++ b/chromium/components/autofill/core/browser/name_field_unittest.cc @@ -5,10 +5,10 @@ #include "components/autofill/core/browser/name_field.h" #include <memory> +#include <vector> #include "base/macros.h" #include "base/memory/ptr_util.h" -#include "base/memory/scoped_vector.h" #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/autofill_field.h" #include "components/autofill/core/browser/autofill_scanner.h" @@ -24,7 +24,7 @@ class NameFieldTest : public testing::Test { NameFieldTest() {} protected: - ScopedVector<AutofillField> list_; + std::vector<std::unique_ptr<AutofillField>> list_; std::unique_ptr<NameField> field_; FieldCandidatesMap field_candidates_map_; @@ -44,17 +44,20 @@ TEST_F(NameFieldTest, FirstMiddleLast) { field.label = ASCIIToUTF16("First Name"); field.name = ASCIIToUTF16("First"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("name1"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name1"))); field.label = ASCIIToUTF16("Middle Name"); field.name = ASCIIToUTF16("Middle"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("name2"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name2"))); field.label = ASCIIToUTF16("Last Name"); field.name = ASCIIToUTF16("Last"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("name3"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name3"))); - AutofillScanner scanner(list_.get()); + AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); field_->AddClassifications(&field_candidates_map_); @@ -78,17 +81,20 @@ TEST_F(NameFieldTest, FirstMiddleLast2) { field.label = base::string16(); field.name = ASCIIToUTF16("firstName"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("name1"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name1"))); field.label = base::string16(); field.name = ASCIIToUTF16("middleName"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("name2"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name2"))); field.label = base::string16(); field.name = ASCIIToUTF16("lastName"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("name3"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name3"))); - AutofillScanner scanner(list_.get()); + AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); field_->AddClassifications(&field_candidates_map_); @@ -112,13 +118,15 @@ TEST_F(NameFieldTest, FirstLast) { field.label = base::string16(); field.name = ASCIIToUTF16("first_name"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("name1"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name1"))); field.label = base::string16(); field.name = ASCIIToUTF16("last_name"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("name2"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name2"))); - AutofillScanner scanner(list_.get()); + AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); field_->AddClassifications(&field_candidates_map_); @@ -138,13 +146,15 @@ TEST_F(NameFieldTest, FirstLast2) { field.label = ASCIIToUTF16("Name"); field.name = ASCIIToUTF16("first_name"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("name1"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name1"))); field.label = ASCIIToUTF16("Name"); field.name = ASCIIToUTF16("last_name"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("name2"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name2"))); - AutofillScanner scanner(list_.get()); + AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); field_->AddClassifications(&field_candidates_map_); @@ -164,17 +174,20 @@ TEST_F(NameFieldTest, FirstLastMiddleWithSpaces) { field.label = ASCIIToUTF16("First Name"); field.name = ASCIIToUTF16("first_name"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("name1"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name1"))); field.label = ASCIIToUTF16("Middle Name"); field.name = ASCIIToUTF16("middle_name"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("name2"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name2"))); field.label = ASCIIToUTF16("Last Name"); field.name = ASCIIToUTF16("last_name"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("name3"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name3"))); - AutofillScanner scanner(list_.get()); + AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); field_->AddClassifications(&field_candidates_map_); @@ -198,13 +211,15 @@ TEST_F(NameFieldTest, FirstLastEmpty) { field.label = ASCIIToUTF16("Name"); field.name = ASCIIToUTF16("first_name"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("name1"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name1"))); - field.label = base::string16(); + field.label = base::string16(); field.name = ASCIIToUTF16("last_name"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("name2"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name2"))); - AutofillScanner scanner(list_.get()); + AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); field_->AddClassifications(&field_candidates_map_); @@ -224,17 +239,20 @@ TEST_F(NameFieldTest, FirstMiddleLastEmpty) { field.label = ASCIIToUTF16("Name"); field.name = ASCIIToUTF16("first_name"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("name1"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name1"))); field.label = base::string16(); field.name = ASCIIToUTF16("middle_name"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("name2"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name2"))); field.label = base::string16(); field.name = ASCIIToUTF16("last_name"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("name3"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name3"))); - AutofillScanner scanner(list_.get()); + AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); field_->AddClassifications(&field_candidates_map_); @@ -258,17 +276,20 @@ TEST_F(NameFieldTest, MiddleInitial) { field.label = ASCIIToUTF16("First Name"); field.name = ASCIIToUTF16("first_name"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("name1"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name1"))); field.label = ASCIIToUTF16("MI"); field.name = ASCIIToUTF16("middle_name"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("name2"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name2"))); field.label = ASCIIToUTF16("Last Name"); field.name = ASCIIToUTF16("last_name"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("name3"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name3"))); - AutofillScanner scanner(list_.get()); + AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); field_->AddClassifications(&field_candidates_map_); @@ -292,13 +313,15 @@ TEST_F(NameFieldTest, MiddleInitialNoLastName) { field.label = ASCIIToUTF16("First Name"); field.name = ASCIIToUTF16("first_name"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("name1"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name1"))); field.label = ASCIIToUTF16("MI"); field.name = ASCIIToUTF16("middle_name"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("name2"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name2"))); - AutofillScanner scanner(list_.get()); + AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_EQ(nullptr, field_.get()); } @@ -311,17 +334,20 @@ TEST_F(NameFieldTest, MiddleInitialAtEnd) { field.label = base::string16(); field.name = ASCIIToUTF16("XXXnameXXXfirst"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("name1"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name1"))); field.label = base::string16(); field.name = ASCIIToUTF16("XXXnameXXXmi"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("name2"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name2"))); field.label = base::string16(); field.name = ASCIIToUTF16("XXXnameXXXlast"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("name3"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name3"))); - AutofillScanner scanner(list_.get()); + AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); field_->AddClassifications(&field_candidates_map_); diff --git a/chromium/components/autofill/core/browser/password_generator.cc b/chromium/components/autofill/core/browser/password_generator.cc index 86877fb647b..def06733a3e 100644 --- a/chromium/components/autofill/core/browser/password_generator.cc +++ b/chromium/components/autofill/core/browser/password_generator.cc @@ -55,7 +55,7 @@ bool VerifyPassword(const std::string& password) { namespace autofill { -const int PasswordGenerator::kDefaultPasswordLength = 12; +const int PasswordGenerator::kDefaultPasswordLength = 15; void ForceFixPassword(std::string* password) { for (char& it : *password) { 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 6fa6a66d947..480f71c8aa4 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 @@ -9,6 +9,7 @@ #include "base/memory/weak_ptr.h" #include "base/message_loop/message_loop.h" #include "base/strings/stringprintf.h" +#include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" #include "components/autofill/core/browser/autofill_test_utils.h" #include "components/autofill/core/browser/credit_card.h" diff --git a/chromium/components/autofill/core/browser/payments/payments_client.cc b/chromium/components/autofill/core/browser/payments/payments_client.cc index 32b2cca0857..5fbeca35005 100644 --- a/chromium/components/autofill/core/browser/payments/payments_client.cc +++ b/chromium/components/autofill/core/browser/payments/payments_client.cc @@ -458,7 +458,7 @@ void PaymentsClient::OnURLFetchComplete(const net::URLFetcher* source) { std::string error_code; std::unique_ptr<base::Value> message_value = base::JSONReader::Read(data); if (message_value.get() && - message_value->IsType(base::Value::TYPE_DICTIONARY)) { + message_value->IsType(base::Value::Type::DICTIONARY)) { response_dict.reset( static_cast<base::DictionaryValue*>(message_value.release())); response_dict->GetString("error.code", &error_code); diff --git a/chromium/components/autofill/core/browser/personal_data_manager.cc b/chromium/components/autofill/core/browser/personal_data_manager.cc index 73ba1d35179..88096ec268a 100644 --- a/chromium/components/autofill/core/browser/personal_data_manager.cc +++ b/chromium/components/autofill/core/browser/personal_data_manager.cc @@ -292,7 +292,15 @@ void PersonalDataManager::Init(scoped_refptr<AutofillWebDataService> database, LoadCreditCards(); database_->AddObserver(this); - is_autofill_profile_dedupe_pending_ = IsAutofillProfileCleanupEnabled(); + + // Check if profile cleanup has already been performed this major version. + is_autofill_profile_cleanup_pending_ = + pref_service_->GetInteger(prefs::kAutofillLastVersionDeduped) >= + atoi(version_info::GetVersionNumber().c_str()); + DVLOG(1) << "Autofill profile cleanup " + << (is_autofill_profile_cleanup_pending_ ? "needs to be" + : "has already been") + << " performed for this version"; } PersonalDataManager::~PersonalDataManager() { @@ -309,7 +317,7 @@ void PersonalDataManager::OnSyncServiceInitialized( syncer::SyncService* sync_service) { // We want to know when, if at all, we need to run autofill profile de- // duplication: now or after waiting until sync has started. - if (!is_autofill_profile_dedupe_pending_) { + if (!is_autofill_profile_cleanup_pending_) { // De-duplication isn't enabled. return; } @@ -419,7 +427,8 @@ void PersonalDataManager::AutofillMultipleChanged() { } void PersonalDataManager::SyncStarted(syncer::ModelType model_type) { - if (model_type == syncer::AUTOFILL_PROFILE) { + if (model_type == syncer::AUTOFILL_PROFILE && + is_autofill_profile_cleanup_pending_) { // This runs as a one-time fix, tracked in syncable prefs. If it has already // run, it is a NOP (other than checking the pref). ApplyProfileUseDatesFix(); @@ -472,7 +481,7 @@ void PersonalDataManager::RecordUseOf(const AutofillDataModel& data_model) { if (credit_card->record_type() == CreditCard::LOCAL_CARD) database_->UpdateCreditCard(*credit_card); else - database_->UpdateServerCardUsageStats(*credit_card); + database_->UpdateServerCardMetadata(*credit_card); Refresh(); return; @@ -485,7 +494,7 @@ void PersonalDataManager::RecordUseOf(const AutofillDataModel& data_model) { if (profile->record_type() == AutofillProfile::LOCAL_PROFILE) database_->UpdateAutofillProfile(*profile); else if (profile->record_type() == AutofillProfile::SERVER_PROFILE) - database_->UpdateServerAddressUsageStats(*profile); + database_->UpdateServerAddressMetadata(*profile); Refresh(); } @@ -636,29 +645,14 @@ void PersonalDataManager::UpdateServerCreditCard( Refresh(); } -void PersonalDataManager::UpdateServerCardBillingAddress( +void PersonalDataManager::UpdateServerCardMetadata( const CreditCard& credit_card) { DCHECK_NE(CreditCard::LOCAL_CARD, credit_card.record_type()); - if (!database_.get()) - return; - - CreditCard* existing_credit_card = nullptr; - for (auto& server_card : server_credit_cards_) { - if (credit_card.server_id() == server_card->server_id()) { - existing_credit_card = server_card.get(); - break; - } - } - if (!existing_credit_card - || existing_credit_card->billing_address_id() == - credit_card.billing_address_id()) { + if (is_off_the_record_ || !database_.get()) return; - } - existing_credit_card->set_billing_address_id( - credit_card.billing_address_id()); - database_->UpdateServerCardBillingAddress(*existing_credit_card); + database_->UpdateServerCardMetadata(credit_card); Refresh(); } @@ -1011,7 +1005,8 @@ std::string PersonalDataManager::MergeProfile( std::string guid = new_profile.guid(); // If we have already saved this address, merge in any missing values. - // Only merge with the first match. + // 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 && @@ -1372,7 +1367,7 @@ bool PersonalDataManager::ImportAddressProfiles(const FormStructure& form) { // Relevant sections for address fields. std::set<std::string> sections; - for (const AutofillField* field : form) { + for (const auto& field : form) { if (field->Type().group() != CREDIT_CARD) sections.insert(field->section()); } @@ -1409,7 +1404,7 @@ bool PersonalDataManager::ImportAddressProfileForSection( std::set<ServerFieldType> types_seen; // Go through each |form| field and attempt to constitute a valid profile. - for (const AutofillField* field : form) { + for (const auto& field : form) { // Reject fields that are not within the specified |section|. if (field->section() != section) continue; @@ -1489,7 +1484,7 @@ bool PersonalDataManager::ImportCreditCard( candidate_credit_card.set_origin(form.source_url().spec()); std::set<ServerFieldType> types_seen; - for (const AutofillField* field : form) { + for (const auto& field : form) { base::string16 value; base::TrimWhitespace(field->value, base::TRIM_ALL, &value); @@ -1625,6 +1620,8 @@ std::vector<Suggestion> PersonalDataManager::GetSuggestionsForCards( suggestion->value = credit_card->TypeAndLastFourDigits(); suggestion->label = credit_card->GetInfo( AutofillType(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR), app_locale_); + if (IsAutofillCreditCardPopupLayoutExperimentEnabled()) + ModifyAutofillCreditCardSuggestion(suggestion); } else if (credit_card->number().empty()) { if (type.GetStorableType() != CREDIT_CARD_NAME_FULL) { suggestion->label = credit_card->GetInfo( @@ -1678,11 +1675,10 @@ void PersonalDataManager::ApplyProfileUseDatesFix() { } bool PersonalDataManager::ApplyDedupingRoutine() { - if (!is_autofill_profile_dedupe_pending_) + if (!is_autofill_profile_cleanup_pending_) return false; - DCHECK(IsAutofillProfileCleanupEnabled()); - is_autofill_profile_dedupe_pending_ = false; + is_autofill_profile_cleanup_pending_ = false; // No need to de-duplicate if there are less than two profiles. if (web_profiles_.size() < 2) { @@ -1703,9 +1699,13 @@ bool PersonalDataManager::ApplyDedupingRoutine() { std::unordered_set<AutofillProfile*> profiles_to_delete; profiles_to_delete.reserve(web_profiles_.size()); - DedupeProfiles(&web_profiles_, &profiles_to_delete); + // Create the map used to update credit card's billing addresses after the + // dedupe. + std::unordered_map<std::string, std::string> guids_merge_map; + + DedupeProfiles(&web_profiles_, &profiles_to_delete, &guids_merge_map); - // Apply the changes to the database. + // Apply the profile changes to the database. for (const auto& profile : web_profiles_) { // If the profile was set to be deleted, remove it from the database. if (profiles_to_delete.count(profile.get())) { @@ -1716,6 +1716,8 @@ bool PersonalDataManager::ApplyDedupingRoutine() { } } + UpdateCardsBillingAddressReference(guids_merge_map); + // Set the pref to the current major version. pref_service_->SetInteger(prefs::kAutofillLastVersionDeduped, current_major_version); @@ -1728,7 +1730,8 @@ bool PersonalDataManager::ApplyDedupingRoutine() { void PersonalDataManager::DedupeProfiles( std::vector<std::unique_ptr<AutofillProfile>>* existing_profiles, - std::unordered_set<AutofillProfile*>* profiles_to_delete) { + std::unordered_set<AutofillProfile*>* profiles_to_delete, + std::unordered_map<std::string, std::string>* guids_merge_map) { AutofillMetrics::LogNumberOfProfilesConsideredForDedupe( existing_profiles->size()); @@ -1782,6 +1785,11 @@ void PersonalDataManager::DedupeProfiles( // and will not accept updates from profile_to_merge. if (existing_profile->SaveAdditionalInfo(*profile_to_merge, app_locale_)) { + // Keep track that a credit card using |profile_to_merge|'s GUID as its + // billing address id should replace it by |existing_profile|'s GUID. + guids_merge_map->insert(std::pair<std::string, std::string>( + profile_to_merge->guid(), existing_profile->guid())); + // Since |profile_to_merge| was a duplicate of |existing_profile| // and was merged successfully, it can now be deleted. profiles_to_delete->insert(profile_to_merge); @@ -1801,4 +1809,47 @@ void PersonalDataManager::DedupeProfiles( profiles_to_delete->size()); } +void PersonalDataManager::UpdateCardsBillingAddressReference( + const std::unordered_map<std::string, std::string>& guids_merge_map) { + /* Here is an example of what the graph might look like. + + A -> B + \ + -> E + / + C -> D + */ + + for (auto& credit_card : local_credit_cards_) { + // If the credit card is not associated with a billing address, skip it. + if (credit_card->billing_address_id().empty()) + break; + + // If the billing address profile associated with the card has been merged, + // replace it by the id of the profile in which it was merged. Repeat the + // process until the billing address has not been merged into another one. + std::unordered_map<std::string, std::string>::size_type nb_guid_changes = 0; + bool was_modified = false; + auto it = guids_merge_map.find(credit_card->billing_address_id()); + while (it != guids_merge_map.end()) { + was_modified = true; + credit_card->set_billing_address_id(it->second); + it = guids_merge_map.find(credit_card->billing_address_id()); + + // Out of abundance of caution. + if (nb_guid_changes > guids_merge_map.size()) { + NOTREACHED(); + // Cancel the changes for that card. + was_modified = false; + break; + } + } + + // If the card was modified, apply the changes to the database. + if (was_modified) { + database_->UpdateCreditCard(*credit_card); + } + } +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/personal_data_manager.h b/chromium/components/autofill/core/browser/personal_data_manager.h index 89508f3a350..165862f6217 100644 --- a/chromium/components/autofill/core/browser/personal_data_manager.h +++ b/chromium/components/autofill/core/browser/personal_data_manager.h @@ -9,6 +9,7 @@ #include <memory> #include <set> #include <string> +#include <unordered_map> #include <unordered_set> #include <vector> @@ -151,9 +152,9 @@ class PersonalDataManager : public KeyedService, // status can be changed. Looks up the card by server ID. virtual void UpdateServerCreditCard(const CreditCard& credit_card); - // Updates the billing address for the server |credit_card|. Looks up the card - // by GUID. - void UpdateServerCardBillingAddress(const CreditCard& credit_card); + // Updates the use stats and billing address id for the server |credit_card|. + // Looks up the card by server_id. + void UpdateServerCardMetadata(const CreditCard& credit_card); // Resets the card for |guid| to the masked state. void ResetFullServerCard(const std::string& guid); @@ -288,10 +289,16 @@ class PersonalDataManager : public KeyedService, FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, AutofillIsEnabledAtStartup); FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest, DedupeProfiles_ProfilesToDelete); + FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest, + DedupeProfiles_GuidsMergeMap); + FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest, + UpdateCardsBillingAddressReference); FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest, ApplyProfileUseDatesFix); FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest, ApplyProfileUseDatesFix_NotAppliedTwice); FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest, + ApplyDedupingRoutine_CardsBillingAddressIdUpdated); + FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest, ApplyDedupingRoutine_MergedProfileValues); FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest, ApplyDedupingRoutine_VerifiedProfileFirst); @@ -472,20 +479,28 @@ class PersonalDataManager : public KeyedService, // Applies the deduping routine once per major version if the feature is // enabled. Calls DedupeProfiles with the content of |web_profiles_| as a // parameter. Removes the profiles to delete from the database and updates the - // others. Returns true if the routine was run. + // others. Also updates the credit cards' billing address references. Returns + // true if the routine was run. bool ApplyDedupingRoutine(); // Goes through all the |existing_profiles| and merges all similar unverified // profiles together. Also discards unverified profiles that are similar to a // verified profile. All the profiles except the results of the merges will be // added to |profile_guids_to_delete|. This routine should be run once per - // major version. + // major version. Records all the merges into the |guids_merge_map|. // // This method should only be called by ApplyDedupingRoutine. It is split for // testing purposes. void DedupeProfiles( std::vector<std::unique_ptr<AutofillProfile>>* existing_profiles, - std::unordered_set<AutofillProfile*>* profile_guids_to_delete); + std::unordered_set<AutofillProfile*>* profile_guids_to_delete, + std::unordered_map<std::string, std::string>* guids_merge_map); + + // Updates the credit cards' billing address reference based on the merges + // that happened during the dedupe, as defined in |guids_merge_map|. Also + // updates the cards entries in the database. + void UpdateCardsBillingAddressReference( + const std::unordered_map<std::string, std::string>& guids_merge_map); const std::string app_locale_; @@ -523,9 +538,8 @@ class PersonalDataManager : public KeyedService, // An observer to listen for changes to prefs::kAutofillWalletImportEnabled. std::unique_ptr<BooleanPrefMember> wallet_enabled_pref_; - // Set to true if autofill profile deduplication is enabled and needs to be - // performed on the next data refresh. - bool is_autofill_profile_dedupe_pending_ = false; + // True if autofill profile cleanup needs to be performed. + bool is_autofill_profile_cleanup_pending_ = false; #if defined(OS_ANDROID) // The context for the request to be used to fetch libaddressinput's address 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 49bee42bf2c..18bb0aebdd4 100644 --- a/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc @@ -15,7 +15,6 @@ #include <vector> #include "base/command_line.h" -#include "base/feature_list.h" #include "base/files/scoped_temp_dir.h" #include "base/guid.h" #include "base/memory/ptr_util.h" @@ -24,7 +23,6 @@ #include "base/strings/utf_string_conversions.h" #include "base/synchronization/waitable_event.h" #include "base/test/histogram_tester.h" -#include "base/test/scoped_feature_list.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" #include "build/build_config.h" @@ -61,6 +59,13 @@ namespace { enum UserMode { USER_MODE_NORMAL, USER_MODE_INCOGNITO }; +const std::string kUTF8MidlineEllipsis = + " " + "\xE2\x80\xA2\xE2\x80\x86" + "\xE2\x80\xA2\xE2\x80\x86" + "\xE2\x80\xA2\xE2\x80\x86" + "\xE2\x80\xA2\xE2\x80\x86"; + ACTION(QuitMainMessageLoop) { base::MessageLoop::current()->QuitWhenIdle(); } @@ -143,7 +148,6 @@ class PersonalDataManagerTest : public testing::Test { // There are no field trials enabled by default. field_trial_list_.reset(); - scoped_feature_list_.reset(); // Reset the deduping pref to its default value. personal_data_->pref_service_->SetInteger( @@ -197,9 +201,7 @@ class PersonalDataManagerTest : public testing::Test { } void EnableAutofillProfileCleanup() { - scoped_feature_list_.reset(new base::test::ScopedFeatureList); - scoped_feature_list_->InitAndEnableFeature(kAutofillProfileCleanup); - personal_data_->is_autofill_profile_dedupe_pending_ = true; + personal_data_->is_autofill_profile_cleanup_pending_ = true; } void SetupReferenceProfile() { @@ -364,7 +366,6 @@ class PersonalDataManagerTest : public testing::Test { std::unique_ptr<base::FieldTrialList> field_trial_list_; scoped_refptr<base::FieldTrial> field_trial_; - std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list_; }; TEST_F(PersonalDataManagerTest, AddProfile) { @@ -3697,8 +3698,7 @@ TEST_F(PersonalDataManagerTest, GetCreditCardSuggestions_NumberMissing) { AutofillType(CREDIT_CARD_NUMBER), /* field_contents= */ base::string16()); ASSERT_EQ(1U, suggestions.size()); - EXPECT_EQ(UTF8ToUTF16("Amex\xC2\xA0\xE2\x8B\xAF" - "8555"), + EXPECT_EQ(UTF8ToUTF16("Amex" + kUTF8MidlineEllipsis + "8555"), suggestions[0].value); EXPECT_EQ(ASCIIToUTF16("04/99"), suggestions[0].label); } @@ -3761,17 +3761,13 @@ TEST_F(PersonalDataManagerTest, GetCreditCardSuggestions_ServerDuplicates) { suggestions = personal_data_->GetCreditCardSuggestions( AutofillType(CREDIT_CARD_NUMBER), /* field_contents= */ base::string16()); ASSERT_EQ(4U, suggestions.size()); - EXPECT_EQ(UTF8ToUTF16("Visa\xC2\xA0\xE2\x8B\xAF" - "9012"), + EXPECT_EQ(UTF8ToUTF16("Visa" + kUTF8MidlineEllipsis + "9012"), suggestions[0].value); - EXPECT_EQ(UTF8ToUTF16("Amex\xC2\xA0\xE2\x8B\xAF" - "8555"), + EXPECT_EQ(UTF8ToUTF16("Amex" + kUTF8MidlineEllipsis + "8555"), suggestions[1].value); - EXPECT_EQ(UTF8ToUTF16("MasterCard\xC2\xA0\xE2\x8B\xAF" - "2109"), + EXPECT_EQ(UTF8ToUTF16("MasterCard" + kUTF8MidlineEllipsis + "2109"), suggestions[2].value); - EXPECT_EQ(UTF8ToUTF16("Visa\xC2\xA0\xE2\x8B\xAF" - "2109"), + EXPECT_EQ(UTF8ToUTF16("Visa" + kUTF8MidlineEllipsis + "2109"), suggestions[3].value); } @@ -4672,8 +4668,10 @@ TEST_F(PersonalDataManagerTest, DedupeProfiles_ProfilesToDelete) { EnableAutofillProfileCleanup(); base::HistogramTester histogram_tester; + std::unordered_map<std::string, std::string> guids_merge_map; std::unordered_set<AutofillProfile*> profiles_to_delete; - personal_data_->DedupeProfiles(&existing_profiles, &profiles_to_delete); + personal_data_->DedupeProfiles(&existing_profiles, &profiles_to_delete, + &guids_merge_map); // 5 profiles were considered for dedupe. histogram_tester.ExpectUniqueSample( "Autofill.NumberOfProfilesConsideredForDedupe", 5, 1); @@ -4696,6 +4694,272 @@ TEST_F(PersonalDataManagerTest, DedupeProfiles_ProfilesToDelete) { EXPECT_EQ(5U, existing_profiles.size()); } +// Tests that DedupeProfiles sets the correct merge mapping for billing address +// id references. +TEST_F(PersonalDataManagerTest, DedupeProfiles_GuidsMergeMap) { + // Create the profile for which to find duplicates. It has the highest + // frecency. + AutofillProfile* profile1 = + new AutofillProfile(base::GenerateGUID(), "https://www.example.com"); + test::SetProfileInfo(profile1, "Homer", "Jay", "Simpson", + "homer.simpson@abc.com", "", "742. Evergreen Terrace", + "", "Springfield", "IL", "91601", "US", "12345678910"); + profile1->set_use_count(9); + + // Create a different profile that should not be deduped (different address). + AutofillProfile* profile2 = + new AutofillProfile(base::GenerateGUID(), "https://www.example.com"); + test::SetProfileInfo(profile2, "Homer", "Jay", "Simpson", + "homer.simpson@abc.com", "Fox", "1234 Other Street", "", + "Springfield", "IL", "91601", "US", "12345678910"); + profile2->set_use_count(7); + + // Create a profile similar to profile1 which should be deduped. + AutofillProfile* profile3 = + new AutofillProfile(base::GenerateGUID(), "https://www.example.com"); + test::SetProfileInfo(profile3, "Homer", "Jay", "Simpson", + "homer.simpson@abc.com", "", "742 Evergreen Terrace", "", + "Springfield", "IL", "91601", "US", "12345678910"); + profile3->set_use_count(5); + + // Create another different profile that should not be deduped (different + // name). + AutofillProfile* profile4 = + new AutofillProfile(base::GenerateGUID(), "https://www.example.com"); + test::SetProfileInfo(profile4, "Marjorie", "Jacqueline", "Simpson", + "homer.simpson@abc.com", "Fox", "742 Evergreen Terrace", + "", "Springfield", "IL", "91601", "US", "12345678910"); + profile4->set_use_count(3); + + // Create another profile similar to profile1. Since that one has the lowest + // frecency, the result of the merge should be in this profile at the end of + // the test. + AutofillProfile* profile5 = + new AutofillProfile(base::GenerateGUID(), "https://www.example.com"); + test::SetProfileInfo(profile5, "Homer", "Jay", "Simpson", + "homer.simpson@abc.com", "Fox", "742 Evergreen Terrace.", + "", "Springfield", "IL", "91601", "US", "12345678910"); + profile5->set_use_count(1); + + // Add the profiles. + std::vector<std::unique_ptr<AutofillProfile>> existing_profiles; + existing_profiles.push_back(base::WrapUnique(profile1)); + existing_profiles.push_back(base::WrapUnique(profile2)); + existing_profiles.push_back(base::WrapUnique(profile3)); + existing_profiles.push_back(base::WrapUnique(profile4)); + existing_profiles.push_back(base::WrapUnique(profile5)); + + // Enable the profile cleanup. + EnableAutofillProfileCleanup(); + + std::unordered_map<std::string, std::string> guids_merge_map; + std::unordered_set<AutofillProfile*> profiles_to_delete; + personal_data_->DedupeProfiles(&existing_profiles, &profiles_to_delete, + &guids_merge_map); + + // The two profile merges should be recorded in the map. + EXPECT_EQ(2U, guids_merge_map.size()); + + // Profile 1 was merged into profile 3. + ASSERT_TRUE(guids_merge_map.count(profile1->guid())); + EXPECT_TRUE(guids_merge_map.at(profile1->guid()) == profile3->guid()); + + // Profile 3 was merged into profile 5. + ASSERT_TRUE(guids_merge_map.count(profile3->guid())); + EXPECT_TRUE(guids_merge_map.at(profile3->guid()) == profile5->guid()); +} + +// Tests that UpdateCardsBillingAddressReference sets the correct billing +// address id as specified in the map. +TEST_F(PersonalDataManagerTest, UpdateCardsBillingAddressReference) { + /* The merges will be as follow: + + A -> B F (not merged) + \ + -> E + / + C -> D + */ + + std::unordered_map<std::string, std::string> guids_merge_map; + guids_merge_map.insert(std::pair<std::string, std::string>("A", "B")); + guids_merge_map.insert(std::pair<std::string, std::string>("C", "D")); + guids_merge_map.insert(std::pair<std::string, std::string>("B", "E")); + guids_merge_map.insert(std::pair<std::string, std::string>("D", "E")); + + // Create cards that use A, D, E and F as their billing address id. + CreditCard* credit_card1 = + new CreditCard(base::GenerateGUID(), "https://www.example.com"); + credit_card1->set_billing_address_id("A"); + CreditCard* credit_card2 = + new CreditCard(base::GenerateGUID(), "https://www.example.com"); + credit_card2->set_billing_address_id("D"); + CreditCard* credit_card3 = + new CreditCard(base::GenerateGUID(), "https://www.example.com"); + credit_card3->set_billing_address_id("E"); + CreditCard* credit_card4 = + new CreditCard(base::GenerateGUID(), "https://www.example.com"); + credit_card4->set_billing_address_id("F"); + + // Add the credit cards to the database. + personal_data_->local_credit_cards_.push_back(base::WrapUnique(credit_card1)); + personal_data_->local_credit_cards_.push_back(base::WrapUnique(credit_card2)); + personal_data_->local_credit_cards_.push_back(base::WrapUnique(credit_card3)); + personal_data_->local_credit_cards_.push_back(base::WrapUnique(credit_card4)); + + personal_data_->UpdateCardsBillingAddressReference(guids_merge_map); + + // The first card's billing address should now be E. + EXPECT_EQ("E", credit_card1->billing_address_id()); + // The second card's billing address should now be E. + EXPECT_EQ("E", credit_card2->billing_address_id()); + // The third card's billing address should still be E. + EXPECT_EQ("E", credit_card3->billing_address_id()); + // The fourth card's billing address should still be F. + EXPECT_EQ("F", credit_card4->billing_address_id()); +} + +// Tests that ApplyDedupingRoutine updates the credit cards' billing address id +// based on the deduped profiles. +TEST_F(PersonalDataManagerTest, + ApplyDedupingRoutine_CardsBillingAddressIdUpdated) { + // A set of 6 profiles will be created. They should merge in this way: + // 1 -> 2 -> 3 + // 4 -> 5 + // 6 + // Set their frencency score so that profile 3 has a higher score than 5, and + // 5 has a higher score than 6. This will ensure a deterministic order when + // verifying results. + + // Create a set of 3 profiles to be merged together. + // Create a profile with a higher frecency score. + AutofillProfile profile1(base::GenerateGUID(), "https://www.example.com"); + test::SetProfileInfo(&profile1, "Homer", "J", "Simpson", + "homer.simpson@abc.com", "", "742. Evergreen Terrace", + "", "Springfield", "IL", "91601", "US", ""); + profile1.set_use_count(12); + profile1.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(1)); + + // Create a profile with a medium frecency score. + AutofillProfile profile2(base::GenerateGUID(), "https://www.example.com"); + test::SetProfileInfo(&profile2, "Homer", "Jay", "Simpson", + "homer.simpson@abc.com", "", "742 Evergreen Terrace", "", + "Springfield", "IL", "91601", "", "12345678910"); + profile2.set_use_count(5); + profile2.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(3)); + + // Create a profile with a lower frecency score. + AutofillProfile profile3(base::GenerateGUID(), "https://www.example.com"); + test::SetProfileInfo(&profile3, "Homer", "J", "Simpson", + "homer.simpson@abc.com", "Fox", "742 Evergreen Terrace.", + "", "Springfield", "IL", "91601", "", ""); + profile3.set_use_count(3); + profile3.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(5)); + + // Create a set of two profiles to be merged together. + // Create a profile with a higher frecency score. + AutofillProfile profile4(base::GenerateGUID(), "https://www.example.com"); + test::SetProfileInfo(&profile4, "Marge", "B", "Simpson", + "marge.simpson@abc.com", "", "742. Evergreen Terrace", + "", "Springfield", "IL", "91601", "US", ""); + profile4.set_use_count(11); + profile4.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(1)); + + // Create a profile with a lower frecency score. + AutofillProfile profile5(base::GenerateGUID(), "https://www.example.com"); + test::SetProfileInfo(&profile5, "Marge", "B", "Simpson", + "marge.simpson@abc.com", "Fox", "742 Evergreen Terrace.", + "", "Springfield", "IL", "91601", "", ""); + profile5.set_use_count(5); + profile5.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(3)); + + // Create a unique profile. + AutofillProfile profile6(base::GenerateGUID(), "https://www.example.com"); + test::SetProfileInfo(&profile6, "Bart", "J", "Simpson", + "bart.simpson@abc.com", "Fox", "742 Evergreen Terrace.", + "", "Springfield", "IL", "91601", "", ""); + profile6.set_use_count(10); + profile6.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(1)); + + // Add three credit cards. Give them a frecency score so that they are + // suggested in order (1, 2, 3). This will ensure a deterministic order for + // verifying results. + CreditCard credit_card1(base::GenerateGUID(), "https://www.example.com"); + test::SetCreditCardInfo(&credit_card1, "Clyde Barrow", + "347666888555" /* American Express */, "04", "2999"); + credit_card1.set_use_count(10); + + CreditCard credit_card2(base::GenerateGUID(), "https://www.example.com"); + test::SetCreditCardInfo(&credit_card2, "John Dillinger", + "423456789012" /* Visa */, "01", "2999"); + credit_card2.set_use_count(5); + + CreditCard credit_card3(base::GenerateGUID(), "https://www.example.com"); + test::SetCreditCardInfo(&credit_card3, "Bonnie Parker", + "518765432109" /* Mastercard */, "12", "2999"); + credit_card3.set_use_count(1); + + // Associate the first card with profile1. + credit_card1.set_billing_address_id(profile1.guid()); + // Associate the second card with profile4. + credit_card2.set_billing_address_id(profile4.guid()); + // Associate the third card with profile6. + credit_card3.set_billing_address_id(profile6.guid()); + + personal_data_->AddProfile(profile1); + personal_data_->AddProfile(profile2); + personal_data_->AddProfile(profile3); + personal_data_->AddProfile(profile4); + personal_data_->AddProfile(profile5); + personal_data_->AddProfile(profile6); + personal_data_->AddCreditCard(credit_card1); + personal_data_->AddCreditCard(credit_card2); + personal_data_->AddCreditCard(credit_card3); + EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()) + .WillOnce(QuitMainMessageLoop()); + base::RunLoop().Run(); + + // Make sure the 6 profiles and 3 credit cards were saved. + EXPECT_EQ(6U, personal_data_->GetProfiles().size()); + EXPECT_EQ(3U, personal_data_->GetCreditCards().size()); + + // Enable the profile cleanup now. Otherwise it would be triggered by the + // calls to AddProfile. + EnableAutofillProfileCleanup(); + + EXPECT_TRUE(personal_data_->ApplyDedupingRoutine()); + EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()) + .WillOnce(QuitMainMessageLoop()); + base::RunLoop().Run(); + + // Get the profiles and cards sorted by frecency to have a deterministic + // order. + std::vector<AutofillProfile*> profiles = + personal_data_->GetProfilesToSuggest(); + std::vector<CreditCard*> credit_cards = + personal_data_->GetCreditCardsToSuggest(); + + // |profile1| should have been merged into |profile2| which should then have + // been merged into |profile3|. |profile4| should have been merged into + // |profile5| and |profile6| should not have merged. Therefore there should be + // 3 profile left. + ASSERT_EQ(3U, profiles.size()); + + // Make sure the remaining profiles are the expected ones. + EXPECT_EQ(profile3.guid(), profiles[0]->guid()); + EXPECT_EQ(profile5.guid(), profiles[1]->guid()); + EXPECT_EQ(profile6.guid(), profiles[2]->guid()); + + // |credit_card1|'s billing address should now be profile 3. + EXPECT_EQ(profile3.guid(), credit_cards[0]->billing_address_id()); + + // |credit_card2|'s billing address should now be profile 5. + EXPECT_EQ(profile5.guid(), credit_cards[1]->billing_address_id()); + + // |credit_card3|'s billing address should still be profile 6. + EXPECT_EQ(profile6.guid(), credit_cards[2]->billing_address_id()); +} + // Tests that ApplyDedupingRoutine merges the profile values correctly, i.e. // never lose information and keep the syntax of the profile with the higher // frecency score. @@ -5363,9 +5627,6 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_OncePerVersion) { "homer.simpson@abc.com", "Fox", "742 Evergreen Terrace.", "", "Springfield", "IL", "91601", "", ""); - // Disable the profile cleanup before adding |profile3|. - scoped_feature_list_.reset(); - personal_data_->AddProfile(profile3); EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()) .WillOnce(QuitMainMessageLoop()); diff --git a/chromium/components/autofill/core/browser/phone_field_unittest.cc b/chromium/components/autofill/core/browser/phone_field_unittest.cc index f270cbfa104..868bb69675f 100644 --- a/chromium/components/autofill/core/browser/phone_field_unittest.cc +++ b/chromium/components/autofill/core/browser/phone_field_unittest.cc @@ -7,10 +7,10 @@ #include <stddef.h> #include <memory> +#include <vector> #include "base/macros.h" #include "base/memory/ptr_util.h" -#include "base/memory/scoped_vector.h" #include "base/stl_util.h" #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/autofill_field.h" @@ -56,7 +56,7 @@ class PhoneFieldTest : public testing::Test { EXPECT_EQ(expected_type, it->second.BestHeuristicType()) << name; } - ScopedVector<AutofillField> list_; + std::vector<std::unique_ptr<AutofillField>> list_; std::unique_ptr<PhoneField> field_; FieldCandidatesMap field_candidates_map_; @@ -65,14 +65,14 @@ class PhoneFieldTest : public testing::Test { }; TEST_F(PhoneFieldTest, Empty) { - AutofillScanner scanner(list_.get()); + AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_EQ(nullptr, field_.get()); } TEST_F(PhoneFieldTest, NonParse) { - list_.push_back(new AutofillField); - AutofillScanner scanner(list_.get()); + list_.push_back(base::MakeUnique<AutofillField>()); + AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_EQ(nullptr, field_.get()); } @@ -86,9 +86,10 @@ TEST_F(PhoneFieldTest, ParseOneLinePhone) { field.form_control_type = field_type; field.label = ASCIIToUTF16("Phone"); field.name = ASCIIToUTF16("phone"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("phone1"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("phone1"))); - AutofillScanner scanner(list_.get()); + AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); field_->AddClassifications(&field_candidates_map_); @@ -105,13 +106,15 @@ TEST_F(PhoneFieldTest, ParseTwoLinePhone) { field.form_control_type = field_type; field.label = ASCIIToUTF16("Area Code"); field.name = ASCIIToUTF16("area code"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("areacode1"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("areacode1"))); field.label = ASCIIToUTF16("Phone"); field.name = ASCIIToUTF16("phone"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("phone2"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("phone2"))); - AutofillScanner scanner(list_.get()); + AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); field_->AddClassifications(&field_candidates_map_); @@ -135,24 +138,28 @@ TEST_F(PhoneFieldTest, ThreePartPhoneNumber) { field.label = ASCIIToUTF16("Phone:"); field.name = ASCIIToUTF16("dayphone1"); field.max_length = 0; - list_.push_back(new AutofillField(field, ASCIIToUTF16("areacode1"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("areacode1"))); field.label = ASCIIToUTF16("-"); field.name = ASCIIToUTF16("dayphone2"); field.max_length = 3; - list_.push_back(new AutofillField(field, ASCIIToUTF16("prefix2"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("prefix2"))); field.label = ASCIIToUTF16("-"); field.name = ASCIIToUTF16("dayphone3"); field.max_length = 4; - list_.push_back(new AutofillField(field, ASCIIToUTF16("suffix3"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("suffix3"))); field.label = ASCIIToUTF16("ext.:"); field.name = ASCIIToUTF16("dayphone4"); field.max_length = 0; - list_.push_back(new AutofillField(field, ASCIIToUTF16("ext4"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("ext4"))); - AutofillScanner scanner(list_.get()); + AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); field_->AddClassifications(&field_candidates_map_); @@ -175,17 +182,20 @@ TEST_F(PhoneFieldTest, ThreePartPhoneNumberPrefixSuffix) { field.form_control_type = field_type; field.label = ASCIIToUTF16("Phone:"); field.name = ASCIIToUTF16("area"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("areacode1"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("areacode1"))); field.label = base::string16(); field.name = ASCIIToUTF16("prefix"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("prefix2"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("prefix2"))); field.label = base::string16(); field.name = ASCIIToUTF16("suffix"); - list_.push_back(new AutofillField(field, ASCIIToUTF16("suffix3"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("suffix3"))); - AutofillScanner scanner(list_.get()); + AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); field_->AddClassifications(&field_candidates_map_); @@ -205,19 +215,22 @@ TEST_F(PhoneFieldTest, ThreePartPhoneNumberPrefixSuffix2) { field.label = ASCIIToUTF16("("); field.name = ASCIIToUTF16("phone1"); field.max_length = 3; - list_.push_back(new AutofillField(field, ASCIIToUTF16("phone1"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("phone1"))); field.label = ASCIIToUTF16(")"); field.name = ASCIIToUTF16("phone2"); field.max_length = 3; - list_.push_back(new AutofillField(field, ASCIIToUTF16("phone2"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("phone2"))); field.label = base::string16(); field.name = ASCIIToUTF16("phone3"); field.max_length = 4; - list_.push_back(new AutofillField(field, ASCIIToUTF16("phone3"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("phone3"))); - AutofillScanner scanner(list_.get()); + AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); field_->AddClassifications(&field_candidates_map_); @@ -239,14 +252,16 @@ TEST_F(PhoneFieldTest, CountryAndCityAndPhoneNumber) { field.label = ASCIIToUTF16("Phone Number"); field.name = ASCIIToUTF16("CountryCode"); field.max_length = 3; - list_.push_back(new AutofillField(field, ASCIIToUTF16("country"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("country"))); field.label = ASCIIToUTF16("Phone Number"); field.name = ASCIIToUTF16("PhoneNumber"); field.max_length = 10; - list_.push_back(new AutofillField(field, ASCIIToUTF16("phone"))); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("phone"))); - AutofillScanner scanner(list_.get()); + AutofillScanner scanner(list_); field_ = Parse(&scanner); ASSERT_NE(nullptr, field_.get()); field_->AddClassifications(&field_candidates_map_); diff --git a/chromium/components/autofill/core/browser/popup_item_ids.h b/chromium/components/autofill/core/browser/popup_item_ids.h index 29be19cb622..7821315f611 100644 --- a/chromium/components/autofill/core/browser/popup_item_ids.h +++ b/chromium/components/autofill/core/browser/popup_item_ids.h @@ -11,7 +11,7 @@ namespace autofill { enum PopupItemId { POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY = 0, - POPUP_ITEM_ID_WARNING_MESSAGE = -1, + POPUP_ITEM_ID_INSECURE_CONTEXT_PAYMENT_DISABLED_MESSAGE = -1, POPUP_ITEM_ID_PASSWORD_ENTRY = -2, POPUP_ITEM_ID_SEPARATOR = -3, POPUP_ITEM_ID_CLEAR_FORM = -4, @@ -20,6 +20,8 @@ enum PopupItemId { POPUP_ITEM_ID_SCAN_CREDIT_CARD = -7, POPUP_ITEM_ID_TITLE = -8, POPUP_ITEM_ID_CREDIT_CARD_SIGNIN_PROMO = -9, + POPUP_ITEM_ID_HTTP_NOT_SECURE_WARNING_MESSAGE = -10, + POPUP_ITEM_ID_USERNAME_ENTRY = -11, }; } // namespace autofill diff --git a/chromium/components/autofill/core/browser/proto/BUILD.gn b/chromium/components/autofill/core/browser/proto/BUILD.gn index ad52e78646b..2a8069f6378 100644 --- a/chromium/components/autofill/core/browser/proto/BUILD.gn +++ b/chromium/components/autofill/core/browser/proto/BUILD.gn @@ -6,6 +6,7 @@ import("//third_party/protobuf/proto_library.gni") proto_library("proto") { sources = [ + "autofill_sync.proto", "server.proto", ] } diff --git a/chromium/components/autofill/core/browser/proto/autofill_sync.proto b/chromium/components/autofill/core/browser/proto/autofill_sync.proto new file mode 100644 index 00000000000..613bdbac022 --- /dev/null +++ b/chromium/components/autofill/core/browser/proto/autofill_sync.proto @@ -0,0 +1,17 @@ +// Copyright 2016 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. + +syntax = "proto2"; + +option optimize_for = LITE_RUNTIME; + +package autofill; + +// Used to convert between autofill::AutofillKey and a std::string that can be +// passed to sync as storage key to uniquely identify an entity of ModelType +// syncer::AUTOFILL. +message AutofillSyncStorageKey { + optional string name = 1; + optional string value = 2; +} diff --git a/chromium/components/autofill/core/browser/suggestion.cc b/chromium/components/autofill/core/browser/suggestion.cc index 22d4ac7c89c..754ede930f8 100644 --- a/chromium/components/autofill/core/browser/suggestion.cc +++ b/chromium/components/autofill/core/browser/suggestion.cc @@ -10,7 +10,8 @@ namespace autofill { Suggestion::Suggestion() : frontend_id(0), - match(PREFIX_MATCH) { + match(PREFIX_MATCH), + is_value_bold(false) { } Suggestion::Suggestion(const Suggestion& other) @@ -19,13 +20,15 @@ Suggestion::Suggestion(const Suggestion& other) value(other.value), label(other.label), icon(other.icon), - match(other.match) { + match(other.match), + is_value_bold(other.is_value_bold) { } Suggestion::Suggestion(const base::string16& v) : frontend_id(0), value(v), - match(PREFIX_MATCH) { + match(PREFIX_MATCH), + is_value_bold(false) { } Suggestion::Suggestion(const std::string& v, @@ -36,7 +39,8 @@ Suggestion::Suggestion(const std::string& v, value(base::UTF8ToUTF16(v)), label(base::UTF8ToUTF16(l)), icon(base::UTF8ToUTF16(i)), - match(PREFIX_MATCH) { + match(PREFIX_MATCH), + is_value_bold(false) { } Suggestion::~Suggestion() { diff --git a/chromium/components/autofill/core/browser/suggestion.h b/chromium/components/autofill/core/browser/suggestion.h index 930ab230f2d..63522debfec 100644 --- a/chromium/components/autofill/core/browser/suggestion.h +++ b/chromium/components/autofill/core/browser/suggestion.h @@ -47,6 +47,7 @@ struct Suggestion { base::string16 label; base::string16 icon; MatchMode match; + bool is_value_bold; // true if |value| should be displayed in bold type face. }; } // namespace autofill diff --git a/chromium/components/autofill/core/browser/test_autofill_client.cc b/chromium/components/autofill/core/browser/test_autofill_client.cc index 5913dedf3bf..7df6c300ce4 100644 --- a/chromium/components/autofill/core/browser/test_autofill_client.cc +++ b/chromium/components/autofill/core/browser/test_autofill_client.cc @@ -11,7 +11,8 @@ namespace autofill { TestAutofillClient::TestAutofillClient() : token_service_(new FakeOAuth2TokenService()), identity_provider_(new FakeIdentityProvider(token_service_.get())), - rappor_service_(new rappor::TestRapporService()) {} + rappor_service_(new rappor::TestRapporServiceImpl()), + form_origin_(GURL("https://example.test")) {} TestAutofillClient::~TestAutofillClient() { } @@ -36,7 +37,7 @@ IdentityProvider* TestAutofillClient::GetIdentityProvider() { return identity_provider_.get(); } -rappor::RapporService* TestAutofillClient::GetRapporService() { +rappor::RapporServiceImpl* TestAutofillClient::GetRapporServiceImpl() { return rappor_service_.get(); } @@ -115,9 +116,9 @@ void TestAutofillClient::DidFillOrPreviewField( void TestAutofillClient::OnFirstUserGestureObserved() { } -bool TestAutofillClient::IsContextSecure(const GURL& form_origin) { +bool TestAutofillClient::IsContextSecure() { // Simplified secure context check for tests. - return form_origin.SchemeIs("https"); + return form_origin_.SchemeIs("https"); } bool TestAutofillClient::ShouldShowSigninPromo() { @@ -126,4 +127,6 @@ bool TestAutofillClient::ShouldShowSigninPromo() { void TestAutofillClient::StartSigninFlow() {} +void TestAutofillClient::ShowHttpNotSecureExplanation() {} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/test_autofill_client.h b/chromium/components/autofill/core/browser/test_autofill_client.h index 013a9030d53..72cdd8c3f57 100644 --- a/chromium/components/autofill/core/browser/test_autofill_client.h +++ b/chromium/components/autofill/core/browser/test_autofill_client.h @@ -33,7 +33,7 @@ class TestAutofillClient : public AutofillClient { PrefService* GetPrefs() override; syncer::SyncService* GetSyncService() override; IdentityProvider* GetIdentityProvider() override; - rappor::RapporService* GetRapporService() override; + rappor::RapporServiceImpl* GetRapporServiceImpl() override; void ShowAutofillSettings() override; void ShowUnmaskPrompt(const CreditCard& card, UnmaskCardReason reason, @@ -67,24 +67,31 @@ class TestAutofillClient : public AutofillClient { void DidFillOrPreviewField(const base::string16& autofilled_value, const base::string16& profile_full_name) override; void OnFirstUserGestureObserved() override; - bool IsContextSecure(const GURL& form_origin) override; + // By default, TestAutofillClient will report that the context is + // secure. This can be adjusted by calling set_form_origin() with an + // http:// URL. + bool IsContextSecure() override; bool ShouldShowSigninPromo() override; void StartSigninFlow() override; + void ShowHttpNotSecureExplanation() override; void SetPrefs(std::unique_ptr<PrefService> prefs) { prefs_ = std::move(prefs); } - rappor::TestRapporService* test_rappor_service() { + rappor::TestRapporServiceImpl* test_rappor_service() { return rappor_service_.get(); } + void set_form_origin(const GURL& url) { form_origin_ = url; } + private: // NULL by default. std::unique_ptr<PrefService> prefs_; std::unique_ptr<FakeOAuth2TokenService> token_service_; std::unique_ptr<FakeIdentityProvider> identity_provider_; - std::unique_ptr<rappor::TestRapporService> rappor_service_; + std::unique_ptr<rappor::TestRapporServiceImpl> rappor_service_; + GURL form_origin_; DISALLOW_COPY_AND_ASSIGN(TestAutofillClient); }; 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 cc7bf73ee44..9da419394b0 100644 --- a/chromium/components/autofill/core/browser/test_autofill_external_delegate.h +++ b/chromium/components/autofill/core/browser/test_autofill_external_delegate.h @@ -9,8 +9,6 @@ namespace autofill { -class AutofillManager; - // Calls the required functions on the given external delegate to cause the // delegate to display a popup. void GenerateTestAutofillPopup( diff --git a/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.cc b/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.cc index a922b5a2c75..435e00caf82 100644 --- a/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.cc +++ b/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.cc @@ -15,6 +15,7 @@ #include "components/autofill/core/browser/autofill_experiments.h" #include "components/autofill/core/browser/autofill_metrics.h" #include "components/autofill/core/browser/ui/card_unmask_prompt_view.h" +#include "components/autofill/core/browser/validation.h" #include "components/autofill/core/common/autofill_pref_names.h" #include "components/prefs/pref_service.h" #include "grit/components_scaled_resources.h" @@ -83,7 +84,7 @@ void CardUnmaskPromptControllerImpl::OnVerificationResult( case AutofillClient::TRY_AGAIN_FAILURE: { error_message = l10n_util::GetStringUTF16( - IDS_AUTOFILL_CARD_UNMASK_PROMPT_ERROR_TRY_AGAIN); + IDS_AUTOFILL_CARD_UNMASK_PROMPT_ERROR_TRY_AGAIN_CVC); break; } @@ -219,15 +220,11 @@ base::string16 CardUnmaskPromptControllerImpl::GetWindowTitle() const { // The iOS UI has less room for the title so it shows a shorter string. return l10n_util::GetStringUTF16(IDS_AUTOFILL_CARD_UNMASK_PROMPT_TITLE); #else - int ids; - if (reason_ == AutofillClient::UNMASK_FOR_AUTOFILL && - ShouldRequestExpirationDate()) { - ids = IDS_AUTOFILL_CARD_UNMASK_PROMPT_UPDATE_TITLE; - } - else { - ids = IDS_AUTOFILL_CARD_UNMASK_PROMPT_TITLE; - } - return l10n_util::GetStringFUTF16(ids, card_.TypeAndLastFourDigits()); + return l10n_util::GetStringFUTF16( + ShouldRequestExpirationDate() + ? IDS_AUTOFILL_CARD_UNMASK_PROMPT_EXPIRED_TITLE + : IDS_AUTOFILL_CARD_UNMASK_PROMPT_TITLE, + card_.TypeAndLastFourDigits()); #endif } @@ -283,10 +280,7 @@ bool CardUnmaskPromptControllerImpl::InputCvcIsValid( const base::string16& input_text) const { base::string16 trimmed_text; base::TrimWhitespace(input_text, base::TRIM_ALL, &trimmed_text); - size_t input_size = card_.type() == kAmericanExpressCard ? 4 : 3; - return trimmed_text.size() == input_size && - base::ContainsOnlyChars(trimmed_text, - base::ASCIIToUTF16("0123456789")); + return IsValidCreditCardSecurityCode(trimmed_text, card_.type()); } bool CardUnmaskPromptControllerImpl::InputExpirationIsValid( @@ -303,23 +297,15 @@ bool CardUnmaskPromptControllerImpl::InputExpirationIsValid( return false; } - if (month_value < 1 || month_value > 12) - return false; - - base::Time::Exploded now; - base::Time::Now().LocalExplode(&now); - // Convert 2 digit year to 4 digit year. - if (year_value < 100) + if (year_value < 100) { + base::Time::Exploded now; + base::Time::Now().LocalExplode(&now); year_value += (now.year / 100) * 100; + } - if (year_value < now.year) - return false; - - if (year_value > now.year) - return true; - - return month_value >= now.month; + return IsValidCreditCardExpirationDate(year_value, month_value, + base::Time::Now()); } base::TimeDelta CardUnmaskPromptControllerImpl::GetSuccessMessageDuration() diff --git a/chromium/components/autofill/core/browser/ui/card_unmask_prompt_view.h b/chromium/components/autofill/core/browser/ui/card_unmask_prompt_view.h index dbedeab35a2..a234739f1c5 100644 --- a/chromium/components/autofill/core/browser/ui/card_unmask_prompt_view.h +++ b/chromium/components/autofill/core/browser/ui/card_unmask_prompt_view.h @@ -10,8 +10,6 @@ namespace autofill { -class CardUnmaskPromptController; - // The cross-platform UI interface which prompts the user to unlock a masked // Wallet instrument (credit card). This object is responsible for its own // lifetime. diff --git a/chromium/components/autofill/core/browser/validation.cc b/chromium/components/autofill/core/browser/validation.cc index b0f0a2a72bc..15a96e6ab22 100644 --- a/chromium/components/autofill/core/browser/validation.cc +++ b/chromium/components/autofill/core/browser/validation.cc @@ -43,6 +43,7 @@ bool IsValidCreditCardNumber(const base::string16& text) { // defined sizes. // [1] http://www.merriampark.com/anatomycc.htm // [2] http://en.wikipedia.org/wiki/Bank_card_number + // CardEditor.isCardNumberLengthMaxium() needs to be kept in sync. const char* const type = CreditCard::GetCreditCardType(text); if (type == kAmericanExpressCard && number.size() != 15) return false; @@ -54,6 +55,8 @@ bool IsValidCreditCardNumber(const base::string16& text) { return false; if (type == kMasterCard && number.size() != 16) return false; + if (type == kMirCard && number.size() != 16) + return false; if (type == kUnionPay && (number.size() < 16 || number.size() > 19)) return false; if (type == kVisaCard && number.size() != 13 && number.size() != 16) @@ -84,25 +87,11 @@ bool IsValidCreditCardNumber(const base::string16& text) { return (sum % 10) == 0; } -bool IsValidCreditCardSecurityCode(const base::string16& text) { - if (text.size() < 3U || text.size() > 4U) - return false; - - for (const base::char16& it : text) { - if (!base::IsAsciiDigit(it)) - return false; - } - return true; -} - bool IsValidCreditCardSecurityCode(const base::string16& code, - const base::string16& number) { - const char* const type = CreditCard::GetCreditCardType(number); - size_t required_length = 3; - if (type == kAmericanExpressCard) - required_length = 4; - - return code.length() == required_length; + const base::StringPiece card_type) { + size_t required_length = card_type == kAmericanExpressCard ? 4 : 3; + return code.length() == required_length && + base::ContainsOnlyChars(code, base::ASCIIToUTF16("0123456789")); } bool IsValidEmailAddress(const base::string16& text) { diff --git a/chromium/components/autofill/core/browser/validation.h b/chromium/components/autofill/core/browser/validation.h index aaaedcd23b8..a09ae75cbb3 100644 --- a/chromium/components/autofill/core/browser/validation.h +++ b/chromium/components/autofill/core/browser/validation.h @@ -6,6 +6,7 @@ #define COMPONENTS_AUTOFILL_CORE_BROWSER_VALIDATION_H_ #include "base/strings/string16.h" +#include "base/strings/string_piece.h" namespace base { class Time; @@ -23,13 +24,10 @@ bool IsValidCreditCardExpirationDate(int year, // Uses the Luhn formula to validate the number. bool IsValidCreditCardNumber(const base::string16& text); -// Returns true if |text| looks like a valid credit card security code. -bool IsValidCreditCardSecurityCode(const base::string16& text); - // Returns true if |code| looks like a valid credit card security code -// for the type of credit card designated by |number|. +// for the given credit card type. bool IsValidCreditCardSecurityCode(const base::string16& code, - const base::string16& number); + const base::StringPiece card_type); // Returns true if |text| looks like a valid e-mail address. bool IsValidEmailAddress(const base::string16& text); diff --git a/chromium/components/autofill/core/browser/validation_unittest.cc b/chromium/components/autofill/core/browser/validation_unittest.cc index c7b880b0a17..798f53462bd 100644 --- a/chromium/components/autofill/core/browser/validation_unittest.cc +++ b/chromium/components/autofill/core/browser/validation_unittest.cc @@ -7,6 +7,7 @@ #include "base/macros.h" #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" +#include "components/autofill/core/browser/credit_card.h" #include "components/autofill/core/browser/validation.h" #include "testing/gtest/include/gtest/gtest.h" @@ -25,6 +26,11 @@ struct IntExpirationDate { const int month; }; +struct SecurityCodeCardTypePair { + const char* security_code; + const char* card_type; +}; + // From https://www.paypalobjects.com/en_US/vhelp/paypalmanager_help/credit_card_numbers.htm const char* const kValidNumbers[] = { "378282246310005", @@ -64,14 +70,16 @@ const IntExpirationDate kInvalidCreditCardIntExpirationDate[] = { { 2015, 13 }, // Not a real month. { 2015, 0 }, // Zero is legal in the CC class but is not a valid date. }; -const char* const kValidCreditCardSecurityCode[] = { - "323", // 3-digit CSC. - "3234", // 4-digit CSC. +const SecurityCodeCardTypePair kValidSecurityCodeCardTypePairs[] = { + { "323", kGenericCard }, // 3-digit CSC. + { "3234", kAmericanExpressCard }, // 4-digit CSC. }; -const char* const kInvalidCreditCardSecurityCode[] = { - "32", // CSC too short. - "12345", // CSC too long. - "asd", // non-numeric CSC. +const SecurityCodeCardTypePair kInvalidSecurityCodeCardTypePairs[] = { + { "32", kGenericCard }, // CSC too short. + { "323", kAmericanExpressCard }, // CSC too short. + { "3234", kGenericCard }, // CSC too long. + { "12345", kAmericanExpressCard }, // CSC too long. + { "asd", kGenericCard }, // non-numeric CSC. }; const char* const kValidEmailAddress[] = { "user@example", @@ -85,10 +93,6 @@ const char* const kInvalidEmailAddress[] = { "user@", "user@=example.com" }; -const char kAmericanExpressCard[] = "341111111111111"; -const char kVisaCard[] = "4111111111111111"; -const char kAmericanExpressCVC[] = "1234"; -const char kVisaCVC[] = "123"; } // namespace TEST(AutofillValidation, IsValidCreditCardNumber) { @@ -117,14 +121,19 @@ TEST(AutofillValidation, IsValidCreditCardIntExpirationDate) { EXPECT_TRUE(!IsValidCreditCardExpirationDate(data.year, data.month, now)); } } + TEST(AutofillValidation, IsValidCreditCardSecurityCode) { - for (const char* valid_code : kValidCreditCardSecurityCode) { - SCOPED_TRACE(valid_code); - EXPECT_TRUE(IsValidCreditCardSecurityCode(ASCIIToUTF16(valid_code))); + for (const auto data : kValidSecurityCodeCardTypePairs) { + SCOPED_TRACE(data.security_code); + SCOPED_TRACE(data.card_type); + EXPECT_TRUE(IsValidCreditCardSecurityCode(ASCIIToUTF16(data.security_code), + data.card_type)); } - for (const char* invalid_code : kInvalidCreditCardSecurityCode) { - SCOPED_TRACE(invalid_code); - EXPECT_FALSE(IsValidCreditCardSecurityCode(ASCIIToUTF16(invalid_code))); + for (const auto data : kInvalidSecurityCodeCardTypePairs) { + SCOPED_TRACE(data.security_code); + SCOPED_TRACE(data.card_type); + EXPECT_FALSE(IsValidCreditCardSecurityCode(ASCIIToUTF16(data.security_code), + data.card_type)); } } @@ -139,19 +148,4 @@ TEST(AutofillValidation, IsValidEmailAddress) { } } -TEST(AutofillValidation, IsValidCreditCardSecurityCodeWithNumber) { - EXPECT_TRUE(IsValidCreditCardSecurityCode( - ASCIIToUTF16(kAmericanExpressCVC), ASCIIToUTF16(kAmericanExpressCard))); - EXPECT_TRUE(IsValidCreditCardSecurityCode( - ASCIIToUTF16(kVisaCVC), ASCIIToUTF16(kVisaCard))); - EXPECT_FALSE(IsValidCreditCardSecurityCode( - ASCIIToUTF16(kVisaCVC), ASCIIToUTF16(kAmericanExpressCard))); - EXPECT_FALSE(IsValidCreditCardSecurityCode( - ASCIIToUTF16(kAmericanExpressCVC), ASCIIToUTF16(kVisaCard))); - EXPECT_TRUE(IsValidCreditCardSecurityCode( - ASCIIToUTF16(kVisaCVC), ASCIIToUTF16(kInvalidNumbers[0]))); - EXPECT_FALSE(IsValidCreditCardSecurityCode( - ASCIIToUTF16(kAmericanExpressCVC), ASCIIToUTF16(kInvalidNumbers[0]))); -} - } // namespace autofill diff --git a/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc new file mode 100644 index 00000000000..0bf47d314ba --- /dev/null +++ b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc @@ -0,0 +1,477 @@ +// Copyright 2016 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/webdata/autocomplete_sync_bridge.h" + +#include <algorithm> +#include <set> +#include <unordered_set> +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/debug/dump_without_crashing.h" +#include "base/memory/ptr_util.h" +#include "base/strings/utf_string_conversions.h" +#include "components/autofill/core/browser/proto/autofill_sync.pb.h" +#include "components/autofill/core/browser/webdata/autofill_metadata_change_list.h" +#include "components/autofill/core/browser/webdata/autofill_table.h" +#include "components/autofill/core/browser/webdata/autofill_webdata_backend.h" +#include "components/autofill/core/browser/webdata/autofill_webdata_service.h" +#include "components/sync/model/entity_data.h" +#include "components/sync/model/model_type_change_processor.h" +#include "components/sync/model/mutable_data_batch.h" +#include "net/base/escape.h" + +using base::Optional; +using base::Time; +using base::debug::DumpWithoutCrashing; +using sync_pb::AutofillSpecifics; +using syncer::EntityChange; +using syncer::EntityChangeList; +using syncer::EntityData; +using syncer::EntityDataMap; +using syncer::MetadataChangeList; +using syncer::ModelError; +using syncer::ModelTypeChangeProcessor; +using syncer::MutableDataBatch; + +namespace autofill { + +namespace { + +const char kAutocompleteEntryNamespaceTag[] = "autofill_entry|"; +const char kAutocompleteTagDelimiter[] = "|"; + +// Simplify checking for optional errors and returning only when present. +#define RETURN_IF_ERROR(x) \ + if (Optional<ModelError> ret_val = x) { \ + return ret_val; \ + } + +void* UserDataKey() { + // Use the address of a static that COMDAT folding won't ever collide + // with something else. + static int user_data_key = 0; + return reinterpret_cast<void*>(&user_data_key); +} + +std::unique_ptr<EntityData> CreateEntityData(const AutofillEntry& entry) { + auto entity_data = base::MakeUnique<EntityData>(); + entity_data->non_unique_name = base::UTF16ToUTF8(entry.key().name()); + AutofillSpecifics* autofill = entity_data->specifics.mutable_autofill(); + autofill->set_name(base::UTF16ToUTF8(entry.key().name())); + autofill->set_value(base::UTF16ToUTF8(entry.key().value())); + autofill->add_usage_timestamp(entry.date_created().ToInternalValue()); + if (entry.date_created() != entry.date_last_used()) + autofill->add_usage_timestamp(entry.date_last_used().ToInternalValue()); + return entity_data; +} + +std::string BuildSerializedStorageKey(const std::string& name, + const std::string& value) { + AutofillSyncStorageKey proto; + proto.set_name(name); + proto.set_value(value); + return proto.SerializeAsString(); +} + +std::string GetStorageKeyFromModel(const AutofillKey& key) { + return BuildSerializedStorageKey(base::UTF16ToUTF8(key.name()), + base::UTF16ToUTF8(key.value())); +} + +AutofillEntry MergeEntryDates(const AutofillEntry& entry1, + const AutofillEntry& entry2) { + DCHECK(entry1.key() == entry2.key()); + return AutofillEntry( + entry1.key(), std::min(entry1.date_created(), entry2.date_created()), + std::max(entry1.date_last_used(), entry2.date_last_used())); +} + +bool ParseStorageKey(const std::string& storage_key, AutofillKey* out_key) { + AutofillSyncStorageKey proto; + if (proto.ParseFromString(storage_key)) { + *out_key = AutofillKey(base::UTF8ToUTF16(proto.name()), + base::UTF8ToUTF16((proto.value()))); + return true; + } else { + return false; + } +} + +AutofillEntry CreateAutofillEntry(const AutofillSpecifics& autofill_specifics) { + AutofillKey key(base::UTF8ToUTF16(autofill_specifics.name()), + base::UTF8ToUTF16(autofill_specifics.value())); + Time date_created, date_last_used; + const google::protobuf::RepeatedField<int64_t>& timestamps = + autofill_specifics.usage_timestamp(); + if (!timestamps.empty()) { + auto iter_pair = std::minmax_element(timestamps.begin(), timestamps.end()); + date_created = Time::FromInternalValue(*iter_pair.first); + date_last_used = Time::FromInternalValue(*iter_pair.second); + } + return AutofillEntry(key, date_created, date_last_used); +} + +// This is used to respond to ApplySyncChanges() and MergeSyncData(). Attempts +// to lazily load local data, and then react to sync data by maintaining +// internal state until flush calls are made, at which point the applicable +// modification should be sent towards local and sync directions. +class SyncDifferenceTracker { + public: + explicit SyncDifferenceTracker(AutofillTable* table) : table_(table) {} + + Optional<ModelError> IncorporateRemoteSpecifics( + const std::string& storage_key, + const AutofillSpecifics& specifics) { + if (!specifics.has_value()) { + // A long time ago autofill had a different format, and it's possible we + // could encounter some of that legacy data. It is not useful to us, + // because an autofill entry with no value will not place any text in a + // form for the user. So drop all of these on the floor. + DVLOG(1) << "Dropping old-style autofill profile change."; + return {}; + } + + const AutofillEntry remote = CreateAutofillEntry(specifics); + DCHECK_EQ(storage_key, GetStorageKeyFromModel(remote.key())); + + Optional<AutofillEntry> local; + if (!ReadEntry(remote.key(), &local)) { + return ModelError(FROM_HERE, "Failed reading from WebDatabase."); + } else if (!local) { + save_to_local_.push_back(remote); + } else if (remote != local.value()) { + if (specifics.usage_timestamp().empty()) { + // Skip merging if there are no timestamps. We don't want to wipe out + // a local value of |date_created| if the remote copy is oddly formed. + save_to_sync_.push_back(local.value()); + } else { + const AutofillEntry merged = MergeEntryDates(local.value(), remote); + save_to_local_.push_back(merged); + save_to_sync_.push_back(merged); + } + unique_to_local_.erase(local.value()); + } + return {}; + } + + Optional<ModelError> IncorporateRemoteDelete(const std::string& storage_key) { + AutofillKey key; + if (!ParseStorageKey(storage_key, &key)) { + return ModelError(FROM_HERE, "Failed parsing storage key."); + } + delete_from_local_.insert(key); + return {}; + } + + Optional<ModelError> FlushToLocal(AutofillWebDataBackend* web_data_backend) { + for (const AutofillKey& key : delete_from_local_) { + if (!table_->RemoveFormElement(key.name(), key.value())) { + return ModelError(FROM_HERE, "Failed deleting from WebDatabase"); + } + } + if (!table_->UpdateAutofillEntries(save_to_local_)) { + return ModelError(FROM_HERE, "Failed updating WebDatabase"); + } + if (!delete_from_local_.empty() || !save_to_local_.empty()) { + web_data_backend->NotifyOfMultipleAutofillChanges(); + } + return {}; + } + + Optional<ModelError> FlushToSync( + bool include_local_only, + std::unique_ptr<MetadataChangeList> metadata_change_list, + ModelTypeChangeProcessor* change_processor) { + for (const AutofillEntry& entry : save_to_sync_) { + change_processor->Put(GetStorageKeyFromModel(entry.key()), + CreateEntityData(entry), + metadata_change_list.get()); + } + if (include_local_only) { + if (!InitializeIfNeeded()) { + return ModelError(FROM_HERE, "Failed reading from WebDatabase."); + } + for (const AutofillEntry& entry : unique_to_local_) { + // This should never be true because only ApplySyncChanges should be + // calling IncorporateRemoteDelete, while only MergeSyncData should be + // passing in true for |include_local_only|. If this requirement + // changes, this DCHECK can change to act as a filter. + DCHECK(delete_from_local_.find(entry.key()) == + delete_from_local_.end()); + change_processor->Put(GetStorageKeyFromModel(entry.key()), + CreateEntityData(entry), + metadata_change_list.get()); + } + } + return static_cast<AutofillMetadataChangeList*>(metadata_change_list.get()) + ->TakeError(); + } + + private: + // There are three major outcomes of this method. + // 1. An error is encountered reading from the db, false is returned. + // 2. The entry is not found, |entry| will not be touched. + // 3. The entry is found, |entry| will be set. + bool ReadEntry(const AutofillKey& key, Optional<AutofillEntry>* entry) { + if (!InitializeIfNeeded()) { + return false; + } + auto iter = unique_to_local_.find(AutofillEntry(key, Time(), Time())); + if (iter != unique_to_local_.end()) { + *entry = *iter; + } + return true; + } + + bool InitializeIfNeeded() { + if (initialized_) { + return true; + } + + std::vector<AutofillEntry> vector; + if (!table_->GetAllAutofillEntries(&vector)) { + return false; + } + + unique_to_local_ = std::set<AutofillEntry>(vector.begin(), vector.end()); + initialized_ = true; + return true; + } + + AutofillTable* table_; + + // This class attempts to lazily load data from |table_|. This field tracks + // if that has happened or not yet. To facilitate this, the first usage of + // |unique_to_local_| should typically be done through ReadEntry(). + bool initialized_ = false; + + // Important to note that because AutofillEntry's operator < simply compares + // contained AutofillKeys, this acts as a map<AutofillKey, AutofillEntry>. + // Shouldn't be accessed until either ReadEntry() or InitializeIfNeeded() is + // called, afterward it will start with all the local data. As sync data is + // encountered entries are removed from here, leaving only entries that exist + // solely on the local client. + std::set<AutofillEntry> unique_to_local_; + + std::set<AutofillKey> delete_from_local_; + std::vector<AutofillEntry> save_to_local_; + + // Contains merged data for entries that existed on both sync and local sides + // and need to be saved back to sync. + std::vector<AutofillEntry> save_to_sync_; + + DISALLOW_COPY_AND_ASSIGN(SyncDifferenceTracker); +}; + +} // namespace + +// static +void AutocompleteSyncBridge::CreateForWebDataServiceAndBackend( + AutofillWebDataService* web_data_service, + AutofillWebDataBackend* web_data_backend) { + web_data_service->GetDBUserData()->SetUserData( + UserDataKey(), + new AutocompleteSyncBridge( + web_data_backend, + base::BindRepeating( + &ModelTypeChangeProcessor::Create, + base::BindRepeating(base::IgnoreResult(&DumpWithoutCrashing))))); +} + +// static +AutocompleteSyncBridge* AutocompleteSyncBridge::FromWebDataService( + AutofillWebDataService* web_data_service) { + return static_cast<AutocompleteSyncBridge*>( + web_data_service->GetDBUserData()->GetUserData(UserDataKey())); +} + +AutocompleteSyncBridge::AutocompleteSyncBridge( + AutofillWebDataBackend* backend, + const ChangeProcessorFactory& change_processor_factory) + : ModelTypeSyncBridge(change_processor_factory, syncer::AUTOFILL), + web_data_backend_(backend), + scoped_observer_(this) { + DCHECK(web_data_backend_); + + scoped_observer_.Add(web_data_backend_); + + LoadMetadata(); +} + +AutocompleteSyncBridge::~AutocompleteSyncBridge() { + DCHECK(thread_checker_.CalledOnValidThread()); +} + +std::unique_ptr<MetadataChangeList> +AutocompleteSyncBridge::CreateMetadataChangeList() { + DCHECK(thread_checker_.CalledOnValidThread()); + return base::MakeUnique<AutofillMetadataChangeList>(GetAutofillTable(), + syncer::AUTOFILL); +} + +Optional<syncer::ModelError> AutocompleteSyncBridge::MergeSyncData( + std::unique_ptr<MetadataChangeList> metadata_change_list, + EntityDataMap entity_data_map) { + DCHECK(thread_checker_.CalledOnValidThread()); + + // TODO(skym, crbug.com/680218): Uncomment and add unit tests. + /*SyncDifferenceTracker tracker(GetAutofillTable()); + for (auto kv : entity_data_map) { + DCHECK(kv.second->specifics.has_autofill()); + RETURN_IF_ERROR(tracker.IncorporateRemoteSpecifics( + kv.first, kv.second->specifics.autofill())); + } + + RETURN_IF_ERROR(tracker.FlushToLocal(web_data_backend_)); + RETURN_IF_ERROR(tracker.FlushToSync(true, std::move(metadata_change_list), + change_processor())); + web_data_backend_->RemoveExpiredFormElements(); + web_data_backend_->NotifyThatSyncHasStarted(syncer::AUTOFILL);*/ + return {}; +} + +Optional<ModelError> AutocompleteSyncBridge::ApplySyncChanges( + std::unique_ptr<MetadataChangeList> metadata_change_list, + EntityChangeList entity_changes) { + DCHECK(thread_checker_.CalledOnValidThread()); + + SyncDifferenceTracker tracker(GetAutofillTable()); + for (const EntityChange& change : entity_changes) { + if (change.type() == EntityChange::ACTION_DELETE) { + RETURN_IF_ERROR(tracker.IncorporateRemoteDelete(change.storage_key())); + } else { + DCHECK(change.data().specifics.has_autofill()); + RETURN_IF_ERROR(tracker.IncorporateRemoteSpecifics( + change.storage_key(), change.data().specifics.autofill())); + } + } + + RETURN_IF_ERROR(tracker.FlushToLocal(web_data_backend_)); + RETURN_IF_ERROR(tracker.FlushToSync(false, std::move(metadata_change_list), + change_processor())); + web_data_backend_->RemoveExpiredFormElements(); + return {}; +} + +void AutocompleteSyncBridge::AutocompleteSyncBridge::GetData( + StorageKeyList storage_keys, + DataCallback callback) { + DCHECK(thread_checker_.CalledOnValidThread()); + std::vector<AutofillEntry> entries; + if (!GetAutofillTable()->GetAllAutofillEntries(&entries)) { + change_processor()->ReportError(FROM_HERE, + "Failed to load entries from table."); + return; + } + + std::unordered_set<std::string> keys_set(storage_keys.begin(), + storage_keys.end()); + auto batch = base::MakeUnique<MutableDataBatch>(); + for (const AutofillEntry& entry : entries) { + std::string key = GetStorageKeyFromModel(entry.key()); + if (keys_set.find(key) != keys_set.end()) { + batch->Put(key, CreateEntityData(entry)); + } + } + callback.Run(std::move(batch)); +} + +void AutocompleteSyncBridge::GetAllData(DataCallback callback) { + DCHECK(thread_checker_.CalledOnValidThread()); + + std::vector<AutofillEntry> entries; + if (!GetAutofillTable()->GetAllAutofillEntries(&entries)) { + change_processor()->ReportError(FROM_HERE, + "Failed to load entries from table."); + return; + } + + auto batch = base::MakeUnique<MutableDataBatch>(); + for (const AutofillEntry& entry : entries) { + batch->Put(GetStorageKeyFromModel(entry.key()), CreateEntityData(entry)); + } + callback.Run(std::move(batch)); +} + +void AutocompleteSyncBridge::ActOnLocalChanges( + const AutofillChangeList& changes) { + if (!change_processor()->IsTrackingMetadata()) { + return; + } + + auto metadata_change_list = base::MakeUnique<AutofillMetadataChangeList>( + GetAutofillTable(), syncer::AUTOFILL); + for (const auto& change : changes) { + const std::string storage_key = GetStorageKeyFromModel(change.key()); + switch (change.type()) { + case AutofillChange::ADD: + case AutofillChange::UPDATE: { + base::Time date_created, date_last_used; + bool success = GetAutofillTable()->GetAutofillTimestamps( + change.key().name(), change.key().value(), &date_created, + &date_last_used); + if (!success) { + change_processor()->ReportError( + FROM_HERE, "Failed reading autofill entry from WebDatabase."); + return; + } + + const AutofillEntry entry(change.key(), date_created, date_last_used); + change_processor()->Put(storage_key, CreateEntityData(entry), + metadata_change_list.get()); + break; + } + case AutofillChange::REMOVE: { + change_processor()->Delete(storage_key, metadata_change_list.get()); + break; + } + } + } + + if (Optional<ModelError> error = metadata_change_list->TakeError()) + change_processor()->ReportError(error.value()); +} + +void AutocompleteSyncBridge::LoadMetadata() { + auto batch = base::MakeUnique<syncer::MetadataBatch>(); + if (!GetAutofillTable()->GetAllSyncMetadata(syncer::AUTOFILL, batch.get())) { + change_processor()->ReportError( + FROM_HERE, "Failed reading autofill metadata from WebDatabase."); + return; + } + change_processor()->ModelReadyToSync(std::move(batch)); +} + +std::string AutocompleteSyncBridge::GetClientTag( + const EntityData& entity_data) { + DCHECK(entity_data.specifics.has_autofill()); + const AutofillSpecifics specifics = entity_data.specifics.autofill(); + return std::string(kAutocompleteEntryNamespaceTag) + + net::EscapePath(specifics.name()) + + std::string(kAutocompleteTagDelimiter) + + net::EscapePath(specifics.value()); +} + +std::string AutocompleteSyncBridge::GetStorageKey( + const EntityData& entity_data) { + DCHECK(entity_data.specifics.has_autofill()); + const AutofillSpecifics specifics = entity_data.specifics.autofill(); + return BuildSerializedStorageKey(specifics.name(), specifics.value()); +} + +// AutofillWebDataServiceObserverOnDBThread implementation. +void AutocompleteSyncBridge::AutofillEntriesChanged( + const AutofillChangeList& changes) { + DCHECK(thread_checker_.CalledOnValidThread()); + ActOnLocalChanges(changes); +} + +AutofillTable* AutocompleteSyncBridge::GetAutofillTable() const { + return AutofillTable::FromWebDatabase(web_data_backend_->GetDatabase()); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h new file mode 100644 index 00000000000..cd350bec322 --- /dev/null +++ b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h @@ -0,0 +1,95 @@ +// Copyright 2016 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_WEBDATA_AUTOCOMPLETE_SYNC_BRIDGE_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOCOMPLETE_SYNC_BRIDGE_H_ + +#include <memory> +#include <string> + +#include "base/macros.h" +#include "base/optional.h" +#include "base/scoped_observer.h" +#include "base/supports_user_data.h" +#include "base/threading/non_thread_safe.h" +#include "components/autofill/core/browser/webdata/autofill_change.h" +#include "components/autofill/core/browser/webdata/autofill_webdata_service_observer.h" +#include "components/sync/model/metadata_change_list.h" +#include "components/sync/model/model_error.h" +#include "components/sync/model/model_type_sync_bridge.h" + +namespace autofill { + +class AutofillTable; +class AutofillWebDataBackend; +class AutofillWebDataService; + +class AutocompleteSyncBridge : public base::SupportsUserData::Data, + public syncer::ModelTypeSyncBridge, + public AutofillWebDataServiceObserverOnDBThread { + public: + AutocompleteSyncBridge(); + AutocompleteSyncBridge( + AutofillWebDataBackend* backend, + const ChangeProcessorFactory& change_processor_factory); + ~AutocompleteSyncBridge() override; + + static void CreateForWebDataServiceAndBackend( + AutofillWebDataService* web_data_service, + AutofillWebDataBackend* web_data_backend); + + static AutocompleteSyncBridge* FromWebDataService( + AutofillWebDataService* web_data_service); + + // syncer::ModelTypeSyncBridge implementation. + std::unique_ptr<syncer::MetadataChangeList> CreateMetadataChangeList() + override; + base::Optional<syncer::ModelError> MergeSyncData( + std::unique_ptr<syncer::MetadataChangeList> metadata_change_list, + syncer::EntityDataMap entity_data_map) override; + base::Optional<syncer::ModelError> ApplySyncChanges( + std::unique_ptr<syncer::MetadataChangeList> metadata_change_list, + syncer::EntityChangeList entity_changes) override; + void GetData(StorageKeyList storage_keys, DataCallback callback) override; + void GetAllData(DataCallback callback) override; + // Generate a tag that uniquely identifies |entity_data| across all data + // types. This is used to identify the entity on the server. The format, which + // must remain the same for server compatibility, is: + // "autofill_entry|$name|$value" where $name and $value are escaped. + std::string GetClientTag(const syncer::EntityData& entity_data) override; + // Generate a string key uniquely identifying |entity_data| in the context of + // local storage. The format, which should stay the same, is $name|$value" + // where $name and $value are escaped. + std::string GetStorageKey(const syncer::EntityData& entity_data) override; + + // AutofillWebDataServiceObserverOnDBThread implementation. + void AutofillEntriesChanged(const AutofillChangeList& changes) override; + + private: + // Returns the table associated with the |web_data_backend_|. + AutofillTable* GetAutofillTable() const; + + // Respond to local autofill entries changing by notifying sync of the + // changes. + void ActOnLocalChanges(const AutofillChangeList& changes); + + // Synchronously load sync metadata from the autofill table and pass it to the + // processor so that it can start tracking changes. + void LoadMetadata(); + + base::ThreadChecker thread_checker_; + + // AutocompleteSyncBridge is owned by |web_data_backend_| through + // SupportsUserData, so it's guaranteed to outlive |this|. + AutofillWebDataBackend* const web_data_backend_; + + ScopedObserver<AutofillWebDataBackend, AutocompleteSyncBridge> + scoped_observer_; + + DISALLOW_COPY_AND_ASSIGN(AutocompleteSyncBridge); +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOCOMPLETE_SYNC_BRIDGE_H_ 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 new file mode 100644 index 00000000000..b12d0ef223f --- /dev/null +++ b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc @@ -0,0 +1,524 @@ +// Copyright 2016 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/webdata/autocomplete_sync_bridge.h" + +#include <algorithm> +#include <map> +#include <vector> + +#include "base/bind.h" +#include "base/files/scoped_temp_dir.h" +#include "base/macros.h" +#include "base/memory/ptr_util.h" +#include "base/run_loop.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "base/threading/thread_task_runner_handle.h" +#include "base/time/time.h" +#include "components/autofill/core/browser/webdata/autofill_entry.h" +#include "components/autofill/core/browser/webdata/autofill_table.h" +#include "components/autofill/core/browser/webdata/autofill_webdata_backend.h" +#include "components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h" +#include "components/sync/model/data_batch.h" +#include "components/sync/model/metadata_batch.h" +#include "components/sync/model/model_error.h" +#include "components/sync/model/recording_model_type_change_processor.h" +#include "components/webdata/common/web_database.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::UTF8ToUTF16; +using base::ScopedTempDir; +using base::Time; +using base::TimeDelta; +using sync_pb::AutofillSpecifics; +using sync_pb::EntitySpecifics; +using syncer::DataBatch; +using syncer::EntityData; +using syncer::EntityDataPtr; +using syncer::EntityChange; +using syncer::EntityChangeList; +using syncer::FakeModelTypeChangeProcessor; +using syncer::KeyAndData; +using syncer::ModelError; +using syncer::ModelType; +using syncer::ModelTypeChangeProcessor; +using syncer::ModelTypeSyncBridge; + +namespace autofill { + +namespace { + +const char kNameFormat[] = "name %d"; +const char kValueFormat[] = "value %d"; + +void VerifyEqual(const AutofillSpecifics& s1, const AutofillSpecifics& s2) { + // Instead of just comparing serialized strings, manually check fields to show + // differences on failure. + EXPECT_EQ(s1.name(), s2.name()); + EXPECT_EQ(s1.value(), s2.value()); + EXPECT_EQ(s1.usage_timestamp().size(), s2.usage_timestamp().size()); + int size = std::min(s1.usage_timestamp().size(), s2.usage_timestamp().size()); + for (int i = 0; i < size; ++i) { + EXPECT_EQ(s1.usage_timestamp(i), s2.usage_timestamp(i)) + << "Values differ at index " << i; + } + // Neither should have any profile data, so we don't have to compare it. + EXPECT_FALSE(s1.has_profile()); + EXPECT_FALSE(s2.has_profile()); +} + +void VerifyDataBatch(std::map<std::string, AutofillSpecifics> expected, + std::unique_ptr<DataBatch> batch) { + while (batch->HasNext()) { + const KeyAndData& pair = batch->Next(); + auto iter = expected.find(pair.first); + ASSERT_NE(iter, expected.end()); + VerifyEqual(iter->second, pair.second->specifics.autofill()); + // Removing allows us to verify we don't see the same item multiple times, + // and that we saw everything we expected. + expected.erase(iter); + } + EXPECT_TRUE(expected.empty()); +} + +AutofillEntry CreateAutofillEntry(const AutofillSpecifics& autofill_specifics) { + AutofillKey key(UTF8ToUTF16(autofill_specifics.name()), + UTF8ToUTF16(autofill_specifics.value())); + Time date_created, date_last_used; + const google::protobuf::RepeatedField<int64_t>& timestamps = + autofill_specifics.usage_timestamp(); + if (!timestamps.empty()) { + date_created = Time::FromInternalValue(*timestamps.begin()); + date_last_used = Time::FromInternalValue(*timestamps.rbegin()); + } + return AutofillEntry(key, date_created, date_last_used); +} + +EntityDataPtr SpecificsToEntity(const AutofillSpecifics& specifics) { + EntityData data; + data.client_tag_hash = "ignored"; + *data.specifics.mutable_autofill() = specifics; + return data.PassToPtr(); +} + +class FakeAutofillBackend : public AutofillWebDataBackend { + public: + FakeAutofillBackend() {} + ~FakeAutofillBackend() override {} + WebDatabase* GetDatabase() override { return db_; } + void AddObserver( + autofill::AutofillWebDataServiceObserverOnDBThread* observer) override {} + void RemoveObserver( + autofill::AutofillWebDataServiceObserverOnDBThread* observer) override {} + void RemoveExpiredFormElements() override {} + void NotifyOfMultipleAutofillChanges() override {} + void NotifyThatSyncHasStarted(ModelType model_type) override {} + void SetWebDatabase(WebDatabase* db) { db_ = db; } + + private: + WebDatabase* db_; +}; + +} // namespace + +class AutocompleteSyncBridgeTest : public testing::Test { + public: + AutocompleteSyncBridgeTest() { + if (temp_dir_.CreateUniqueTempDir()) { + db_.AddTable(&table_); + db_.Init(temp_dir_.GetPath().AppendASCII("SyncTestWebDatabase")); + backend_.SetWebDatabase(&db_); + + sync_pb::ModelTypeState model_type_state; + model_type_state.set_initial_sync_done(true); + table_.UpdateModelTypeState(syncer::AUTOFILL, model_type_state); + + bridge_.reset(new AutocompleteSyncBridge( + &backend_, + base::Bind( + &AutocompleteSyncBridgeTest::CreateModelTypeChangeProcessor, + base::Unretained(this)))); + } + } + ~AutocompleteSyncBridgeTest() override {} + + void SaveSpecificsToTable( + const std::vector<AutofillSpecifics>& specifics_list) { + std::vector<AutofillEntry> new_entries; + for (const auto& specifics : specifics_list) { + new_entries.push_back(CreateAutofillEntry(specifics)); + } + table_.UpdateAutofillEntries(new_entries); + } + + AutofillSpecifics CreateSpecifics(const std::string& name, + const std::string& value, + const std::vector<int>& timestamps) { + AutofillSpecifics specifics; + specifics.set_name(name); + specifics.set_value(value); + for (int timestamp : timestamps) { + specifics.add_usage_timestamp( + Time::FromTimeT(timestamp).ToInternalValue()); + } + return specifics; + } + + AutofillSpecifics CreateSpecifics(int suffix, + const std::vector<int>& timestamps) { + return CreateSpecifics(base::StringPrintf(kNameFormat, suffix), + base::StringPrintf(kValueFormat, suffix), + timestamps); + } + + AutofillSpecifics CreateSpecifics(int suffix) { + return CreateSpecifics(suffix, std::vector<int>{0}); + } + + std::string GetStorageKey(const AutofillSpecifics& specifics) { + std::string key = + bridge()->GetStorageKey(SpecificsToEntity(specifics).value()); + EXPECT_FALSE(key.empty()); + return key; + } + + EntityChangeList EntityAddList( + const std::vector<AutofillSpecifics>& specifics_vector) { + EntityChangeList changes; + for (const auto& specifics : specifics_vector) { + changes.push_back(EntityChange::CreateAdd(GetStorageKey(specifics), + SpecificsToEntity(specifics))); + } + return changes; + } + + void VerifyApplyChanges(const std::vector<EntityChange>& changes) { + const auto error = bridge()->ApplySyncChanges( + bridge()->CreateMetadataChangeList(), changes); + EXPECT_FALSE(error); + } + + void VerifyApplyAdds(const std::vector<AutofillSpecifics>& specifics) { + VerifyApplyChanges(EntityAddList(specifics)); + } + + std::map<std::string, AutofillSpecifics> ExpectedMap( + const std::vector<AutofillSpecifics>& specifics_vector) { + std::map<std::string, AutofillSpecifics> map; + for (const auto& specifics : specifics_vector) { + map[GetStorageKey(specifics)] = specifics; + } + return map; + } + + void VerifyAllData(const std::vector<AutofillSpecifics>& expected) { + bridge()->GetAllData(base::Bind(&VerifyDataBatch, ExpectedMap(expected))); + } + + void VerifyProcessorRecordedPut(const AutofillSpecifics& specifics, + int position = 0) { + const std::string storage_key = GetStorageKey(specifics); + auto recorded_specifics_iterator = + processor()->put_multimap().find(storage_key); + + EXPECT_NE(processor()->put_multimap().end(), recorded_specifics_iterator); + while (position > 0) { + recorded_specifics_iterator++; + EXPECT_NE(processor()->put_multimap().end(), recorded_specifics_iterator); + position--; + } + + AutofillSpecifics recorded_specifics = + recorded_specifics_iterator->second->specifics.autofill(); + VerifyEqual(recorded_specifics, specifics); + } + + AutocompleteSyncBridge* bridge() { return bridge_.get(); } + + syncer::RecordingModelTypeChangeProcessor* processor() { return processor_; } + + AutofillTable* table() { return &table_; } + + private: + std::unique_ptr<syncer::ModelTypeChangeProcessor> + CreateModelTypeChangeProcessor(syncer::ModelType type, + syncer::ModelTypeSyncBridge* bridge) { + auto processor = + base::MakeUnique<syncer::RecordingModelTypeChangeProcessor>(); + processor_ = processor.get(); + return std::move(processor); + } + + ScopedTempDir temp_dir_; + base::MessageLoop message_loop_; + FakeAutofillBackend backend_; + AutofillTable table_; + WebDatabase db_; + std::unique_ptr<AutocompleteSyncBridge> bridge_; + // A non-owning pointer to the processor given to the bridge. Will be null + // before being given to the bridge, to make ownership easier. + syncer::RecordingModelTypeChangeProcessor* processor_ = nullptr; + + DISALLOW_COPY_AND_ASSIGN(AutocompleteSyncBridgeTest); +}; + +TEST_F(AutocompleteSyncBridgeTest, GetClientTag) { + // TODO(skym, crbug.com/675991): Implementation. +} + +TEST_F(AutocompleteSyncBridgeTest, GetStorageKey) { + std::string key = GetStorageKey(CreateSpecifics(1)); + EXPECT_EQ(key, GetStorageKey(CreateSpecifics(1))); + EXPECT_NE(key, GetStorageKey(CreateSpecifics(2))); +} + +// Timestamps should not affect storage keys. +TEST_F(AutocompleteSyncBridgeTest, GetStorageKeyTimestamp) { + AutofillSpecifics specifics = CreateSpecifics(1); + std::string key = GetStorageKey(specifics); + + specifics.add_usage_timestamp(1); + EXPECT_EQ(key, GetStorageKey(specifics)); + + specifics.add_usage_timestamp(0); + EXPECT_EQ(key, GetStorageKey(specifics)); + + specifics.add_usage_timestamp(-1); + EXPECT_EQ(key, GetStorageKey(specifics)); +} + +// Verify that the \0 character is respected as a difference. +TEST_F(AutocompleteSyncBridgeTest, GetStorageKeyNull) { + AutofillSpecifics specifics; + std::string key = GetStorageKey(specifics); + + specifics.set_value(std::string("\0", 1)); + EXPECT_NE(key, GetStorageKey(specifics)); +} + +// The storage key should never accidentally change for existing data. This +// would cause lookups to fail and either lose or duplicate user data. It should +// be possible for the model type to migrate storage key formats, but doing so +// would need to be done very carefully. +TEST_F(AutocompleteSyncBridgeTest, GetStorageKeyFixed) { + EXPECT_EQ("\n\x6name 1\x12\avalue 1", GetStorageKey(CreateSpecifics(1))); + EXPECT_EQ("\n\x6name 2\x12\avalue 2", GetStorageKey(CreateSpecifics(2))); + // This literal contains the null terminating character, which causes + // std::string to stop copying early if we don't tell it how much to read. + EXPECT_EQ(std::string("\n\0\x12\0", 4), GetStorageKey(AutofillSpecifics())); + AutofillSpecifics specifics; + specifics.set_name("\xEC\xA4\x91"); + specifics.set_value("\xD0\x80"); + EXPECT_EQ("\n\x3\xEC\xA4\x91\x12\x2\xD0\x80", GetStorageKey(specifics)); +} + +TEST_F(AutocompleteSyncBridgeTest, GetData) { + const AutofillSpecifics specifics1 = CreateSpecifics(1); + const AutofillSpecifics specifics2 = CreateSpecifics(2); + const AutofillSpecifics specifics3 = CreateSpecifics(3); + SaveSpecificsToTable({specifics1, specifics2, specifics3}); + bridge()->GetData( + {GetStorageKey(specifics1), GetStorageKey(specifics3)}, + base::Bind(&VerifyDataBatch, ExpectedMap({specifics1, specifics3}))); +} + +TEST_F(AutocompleteSyncBridgeTest, GetDataNotExist) { + const AutofillSpecifics specifics1 = CreateSpecifics(1); + const AutofillSpecifics specifics2 = CreateSpecifics(2); + const AutofillSpecifics specifics3 = CreateSpecifics(3); + SaveSpecificsToTable({specifics1, specifics2}); + bridge()->GetData( + {GetStorageKey(specifics1), GetStorageKey(specifics2), + GetStorageKey(specifics3)}, + base::Bind(&VerifyDataBatch, ExpectedMap({specifics1, specifics2}))); +} + +TEST_F(AutocompleteSyncBridgeTest, GetAllData) { + const AutofillSpecifics specifics1 = CreateSpecifics(1); + const AutofillSpecifics specifics2 = CreateSpecifics(2); + const AutofillSpecifics specifics3 = CreateSpecifics(3); + SaveSpecificsToTable({specifics1, specifics2, specifics3}); + VerifyAllData({specifics1, specifics2, specifics3}); +} + +TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesEmpty) { + // TODO(skym, crbug.com/672619): Ideally would like to verify that the db is + // not accessed. + VerifyApplyAdds(std::vector<AutofillSpecifics>()); +} + +TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesSimple) { + AutofillSpecifics specifics1 = CreateSpecifics(1); + AutofillSpecifics specifics2 = CreateSpecifics(2); + ASSERT_NE(specifics1.SerializeAsString(), specifics2.SerializeAsString()); + ASSERT_NE(GetStorageKey(specifics1), GetStorageKey(specifics2)); + + VerifyApplyAdds({specifics1, specifics2}); + VerifyAllData({specifics1, specifics2}); + + VerifyApplyChanges({EntityChange::CreateDelete(GetStorageKey(specifics1))}); + VerifyAllData({specifics2}); +} + +// Should be resilient to deleting and updating non-existent things, and adding +// existing ones. +TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesWrongChangeType) { + AutofillSpecifics specifics = CreateSpecifics(1, {1}); + VerifyApplyChanges({EntityChange::CreateDelete(GetStorageKey(specifics))}); + VerifyAllData(std::vector<AutofillSpecifics>()); + + VerifyApplyChanges({EntityChange::CreateUpdate( + GetStorageKey(specifics), SpecificsToEntity(specifics))}); + VerifyAllData({specifics}); + + specifics.add_usage_timestamp(Time::FromTimeT(2).ToInternalValue()); + VerifyApplyAdds({specifics}); + VerifyAllData({specifics}); +} + +// The format in the table has a fixed 2 timestamp format. Round tripping is +// lossy and the middle value should be thrown out. +TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesThreeTimestamps) { + VerifyApplyAdds({CreateSpecifics(1, {1, 2, 3})}); + VerifyAllData({CreateSpecifics(1, {1, 3})}); +} + +// The correct format of timestamps is that the first should be smaller and the +// second should be larger. Bad foreign data should be repaired. +TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesWrongOrder) { + VerifyApplyAdds({CreateSpecifics(1, {3, 2})}); + VerifyAllData({CreateSpecifics(1, {2, 3})}); +} + +// In a minor attempt to save bandwidth, we only send one of the two timestamps +// when they share a value. +TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesRepeatedTime) { + VerifyApplyAdds({CreateSpecifics(1, {2, 2})}); + VerifyAllData({CreateSpecifics(1, {2})}); +} + +// Again, the format in the table is lossy, and cannot distinguish between no +// time, and valid time zero. +TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesNoTime) { + VerifyApplyAdds({CreateSpecifics(1, std::vector<int>())}); + VerifyAllData({CreateSpecifics(1, {0})}); +} + +// If has_value() returns false, then the specifics are determined to be old +// style and ignored. +TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesNoValue) { + AutofillSpecifics input = CreateSpecifics(1, {2, 3}); + input.clear_value(); + VerifyApplyAdds({input}); + VerifyAllData(std::vector<AutofillSpecifics>()); +} + +// Should be treated the same as an empty string name. This inconsistency is +// being perpetuated from the previous sync integration. +TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesNoName) { + AutofillSpecifics input = CreateSpecifics(1, {2, 3}); + input.clear_name(); + VerifyApplyAdds({input}); + VerifyAllData({input}); +} + +// UTF-8 characters should not be dropped when round tripping, including middle +// of string \0 characters. +TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesUTF) { + const AutofillSpecifics specifics = + CreateSpecifics(std::string("\n\0\x12\0", 4), "\xEC\xA4\x91", {1}); + VerifyApplyAdds({specifics}); + VerifyAllData({specifics}); +} + +// Timestamps should always use the oldest creation time, and the most recent +// usage time. +TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesMinMaxTimestamps) { + const AutofillSpecifics initial = CreateSpecifics(1, {3, 6}); + VerifyApplyAdds({initial}); + VerifyAllData({initial}); + + VerifyApplyAdds({CreateSpecifics(1, {2, 5})}); + VerifyAllData({CreateSpecifics(1, {2, 6})}); + + VerifyApplyAdds({CreateSpecifics(1, {4, 7})}); + VerifyAllData({CreateSpecifics(1, {2, 7})}); +} + +// An error should be generated when parsing the storage key happens. This +// should never happen in practice because storage keys should be generated at +// runtime by the bridge and not loaded from disk. +TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesBadStorageKey) { + const auto error = bridge()->ApplySyncChanges( + bridge()->CreateMetadataChangeList(), + {EntityChange::CreateDelete("bogus storage key")}); + EXPECT_TRUE(error); +} + +TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesDatabaseFailure) { + // TODO(skym, crbug.com/672619): Should have tests that get false back when + // making db calls and verify the errors are propagated up. +} + +TEST_F(AutocompleteSyncBridgeTest, LocalEntriesAdded) { + const AutofillSpecifics added_specifics1 = CreateSpecifics(1, {2, 3}); + const AutofillSpecifics added_specifics2 = CreateSpecifics(2, {2, 3}); + + const AutofillEntry added_entry1 = CreateAutofillEntry(added_specifics1); + const AutofillEntry added_entry2 = CreateAutofillEntry(added_specifics2); + + table()->UpdateAutofillEntries({added_entry1, added_entry2}); + + bridge()->AutofillEntriesChanged( + {AutofillChange(AutofillChange::ADD, added_entry1.key()), + AutofillChange(AutofillChange::ADD, added_entry2.key())}); + + EXPECT_EQ(2u, processor()->put_multimap().size()); + + VerifyProcessorRecordedPut(added_specifics1); + VerifyProcessorRecordedPut(added_specifics2); +} + +TEST_F(AutocompleteSyncBridgeTest, LocalEntryAddedThenUpdated) { + const AutofillSpecifics added_specifics = CreateSpecifics(1, {2, 3}); + const AutofillEntry added_entry = CreateAutofillEntry(added_specifics); + table()->UpdateAutofillEntries({added_entry}); + + bridge()->AutofillEntriesChanged( + {AutofillChange(AutofillChange::ADD, added_entry.key())}); + + EXPECT_EQ(1u, processor()->put_multimap().size()); + + VerifyProcessorRecordedPut(added_specifics); + + const AutofillSpecifics updated_specifics = CreateSpecifics(1, {2, 4}); + const AutofillEntry updated_entry = CreateAutofillEntry(updated_specifics); + table()->UpdateAutofillEntries({updated_entry}); + + bridge()->AutofillEntriesChanged( + {AutofillChange(AutofillChange::UPDATE, updated_entry.key())}); + + VerifyProcessorRecordedPut(updated_specifics, 1); +} + +TEST_F(AutocompleteSyncBridgeTest, LocalEntryDeleted) { + const AutofillSpecifics deleted_specifics = CreateSpecifics(1, {2, 3}); + const AutofillEntry deleted_entry = CreateAutofillEntry(deleted_specifics); + const std::string storage_key = GetStorageKey(deleted_specifics); + + bridge()->AutofillEntriesChanged( + {AutofillChange(AutofillChange::REMOVE, deleted_entry.key())}); + + EXPECT_EQ(1u, processor()->delete_set().size()); + EXPECT_NE(processor()->delete_set().end(), + processor()->delete_set().find(storage_key)); +} + +TEST_F(AutocompleteSyncBridgeTest, LoadMetadataCalled) { + EXPECT_NE(processor()->metadata(), nullptr); + EXPECT_TRUE(processor()->metadata()->GetModelTypeState().initial_sync_done()); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/webdata/autocomplete_syncable_service.cc b/chromium/components/autofill/core/browser/webdata/autocomplete_syncable_service.cc index 76fb7fbdbcc..aff312184ea 100644 --- a/chromium/components/autofill/core/browser/webdata/autocomplete_syncable_service.cc +++ b/chromium/components/autofill/core/browser/webdata/autocomplete_syncable_service.cc @@ -317,7 +317,7 @@ void AutocompleteSyncableService::CreateOrUpdateEntry( if (it == loaded_data->end()) { // New entry. base::Time date_created, date_last_used; - if (timestamps.size() > 0) { + if (!timestamps.empty()) { date_created = base::Time::FromInternalValue(*timestamps.begin()); date_last_used = base::Time::FromInternalValue(*timestamps.rbegin()); } diff --git a/chromium/components/autofill/core/browser/webdata/autofill_data_type_controller.cc b/chromium/components/autofill/core/browser/webdata/autofill_data_type_controller.cc index d1399bfc3a2..fc3beeae20d 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_data_type_controller.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_data_type_controller.cc @@ -4,6 +4,8 @@ #include "components/autofill/core/browser/webdata/autofill_data_type_controller.h" +#include <utility> + #include "base/bind.h" #include "base/metrics/histogram.h" #include "components/autofill/core/browser/webdata/autocomplete_syncable_service.h" @@ -14,18 +16,17 @@ namespace browser_sync { AutofillDataTypeController::AutofillDataTypeController( - const scoped_refptr<base::SingleThreadTaskRunner>& db_thread, + scoped_refptr<base::SingleThreadTaskRunner> db_thread, const base::Closure& dump_stack, syncer::SyncClient* sync_client, const scoped_refptr<autofill::AutofillWebDataService>& web_data_service) - : NonUIDataTypeController(syncer::AUTOFILL, dump_stack, sync_client), - db_thread_(db_thread), + : AsyncDirectoryTypeController(syncer::AUTOFILL, + dump_stack, + sync_client, + syncer::GROUP_DB, + std::move(db_thread)), web_data_service_(web_data_service) {} -syncer::ModelSafeGroup AutofillDataTypeController::model_safe_group() const { - return syncer::GROUP_DB; -} - void AutofillDataTypeController::WebDatabaseLoaded() { DCHECK(CalledOnValidThread()); DCHECK_EQ(MODEL_STARTING, state()); @@ -37,13 +38,6 @@ AutofillDataTypeController::~AutofillDataTypeController() { DCHECK(CalledOnValidThread()); } -bool AutofillDataTypeController::PostTaskOnBackendThread( - const tracked_objects::Location& from_here, - const base::Closure& task) { - DCHECK(CalledOnValidThread()); - return db_thread_->PostTask(from_here, task); -} - bool AutofillDataTypeController::StartModels() { DCHECK(CalledOnValidThread()); DCHECK_EQ(MODEL_STARTING, state()); diff --git a/chromium/components/autofill/core/browser/webdata/autofill_data_type_controller.h b/chromium/components/autofill/core/browser/webdata/autofill_data_type_controller.h index 90559399957..bccdbf7d9f7 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_data_type_controller.h +++ b/chromium/components/autofill/core/browser/webdata/autofill_data_type_controller.h @@ -11,7 +11,7 @@ #include "base/gtest_prod_util.h" #include "base/macros.h" #include "base/memory/ref_counted.h" -#include "components/sync/driver/non_ui_data_type_controller.h" +#include "components/sync/driver/async_directory_type_controller.h" namespace autofill { class AutofillWebDataService; @@ -20,23 +20,18 @@ class AutofillWebDataService; namespace browser_sync { // A class that manages the startup and shutdown of autofill sync. -class AutofillDataTypeController : public syncer::NonUIDataTypeController { +class AutofillDataTypeController : public syncer::AsyncDirectoryTypeController { public: // |dump_stack| is called when an unrecoverable error occurs. AutofillDataTypeController( - const scoped_refptr<base::SingleThreadTaskRunner>& db_thread, + scoped_refptr<base::SingleThreadTaskRunner> db_thread, const base::Closure& dump_stack, syncer::SyncClient* sync_client, const scoped_refptr<autofill::AutofillWebDataService>& web_data_service); ~AutofillDataTypeController() override; - // NonUIDataTypeController implementation. - syncer::ModelSafeGroup model_safe_group() const override; - protected: - // NonUIDataTypeController implementation. - bool PostTaskOnBackendThread(const tracked_objects::Location& from_here, - const base::Closure& task) override; + // AsyncDirectoryTypeController implementation. bool StartModels() override; private: @@ -47,9 +42,6 @@ class AutofillDataTypeController : public syncer::NonUIDataTypeController { // Callback once WebDatabase has loaded. void WebDatabaseLoaded(); - // A reference to the DB thread's task runner. - const scoped_refptr<base::SingleThreadTaskRunner> db_thread_; - // A reference to the AutofillWebDataService for this controller. scoped_refptr<autofill::AutofillWebDataService> web_data_service_; diff --git a/chromium/components/autofill/core/browser/webdata/autofill_data_type_controller_unittest.cc b/chromium/components/autofill/core/browser/webdata/autofill_data_type_controller_unittest.cc index b0ac2cd33df..90aa68b77d8 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_data_type_controller_unittest.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_data_type_controller_unittest.cc @@ -10,6 +10,7 @@ #include "base/callback.h" #include "base/compiler_specific.h" #include "base/macros.h" +#include "base/memory/ptr_util.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" #include "base/message_loop/message_loop.h" diff --git a/chromium/components/autofill/core/browser/webdata/autofill_entry.cc b/chromium/components/autofill/core/browser/webdata/autofill_entry.cc index 83fd639bbbd..58494a7c93e 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_entry.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_entry.cc @@ -38,6 +38,8 @@ bool AutofillKey::operator<(const AutofillKey& key) const { return std::tie(name_, value_) < std::tie(key.name(), key.value()); } +AutofillEntry::AutofillEntry() {} + AutofillEntry::AutofillEntry(const AutofillKey& key, const base::Time& date_created, const base::Time& date_last_used) @@ -53,6 +55,10 @@ bool AutofillEntry::operator==(const AutofillEntry& entry) const { date_last_used() == entry.date_last_used(); } +bool AutofillEntry::operator!=(const AutofillEntry& entry) const { + return !(*this == entry); +} + bool AutofillEntry::operator<(const AutofillEntry& entry) const { return key_ < entry.key(); } diff --git a/chromium/components/autofill/core/browser/webdata/autofill_entry.h b/chromium/components/autofill/core/browser/webdata/autofill_entry.h index 1edc4010dd5..4a934dd4510 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_entry.h +++ b/chromium/components/autofill/core/browser/webdata/autofill_entry.h @@ -31,6 +31,7 @@ class AutofillKey { class AutofillEntry { public: + AutofillEntry(); AutofillEntry(const AutofillKey& key, const base::Time& date_created, const base::Time& date_last_used); @@ -41,6 +42,7 @@ class AutofillEntry { const base::Time& date_last_used() const { return date_last_used_; } bool operator==(const AutofillEntry& entry) const; + bool operator!=(const AutofillEntry& entry) const; bool operator<(const AutofillEntry& entry) const; private: diff --git a/chromium/components/autofill/core/browser/webdata/autofill_metadata_change_list.cc b/chromium/components/autofill/core/browser/webdata/autofill_metadata_change_list.cc new file mode 100644 index 00000000000..24876e4a0cf --- /dev/null +++ b/chromium/components/autofill/core/browser/webdata/autofill_metadata_change_list.cc @@ -0,0 +1,75 @@ +// Copyright 2016 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/webdata/autofill_metadata_change_list.h" + +#include "base/location.h" + +using base::Optional; +using syncer::ModelError; + +namespace autofill { + +AutofillMetadataChangeList::AutofillMetadataChangeList(AutofillTable* table, + syncer::ModelType type) + : table_(table), type_(type) { + DCHECK(table_); + // This should be changed as new autofill types are converted to USS. + DCHECK_EQ(syncer::AUTOFILL, type_); +} + +AutofillMetadataChangeList::~AutofillMetadataChangeList() { + DCHECK(!error_); +} + +void AutofillMetadataChangeList::UpdateModelTypeState( + const sync_pb::ModelTypeState& model_type_state) { + if (error_) { + return; + } + + if (!table_->UpdateModelTypeState(type_, model_type_state)) { + error_ = ModelError(FROM_HERE, "Failed to update ModelTypeState."); + } +} + +void AutofillMetadataChangeList::ClearModelTypeState() { + if (error_) { + return; + } + + if (!table_->ClearModelTypeState(type_)) { + error_ = ModelError(FROM_HERE, "Failed to clear ModelTypeState."); + } +} + +void AutofillMetadataChangeList::UpdateMetadata( + const std::string& storage_key, + const sync_pb::EntityMetadata& metadata) { + if (error_) { + return; + } + + if (!table_->UpdateSyncMetadata(type_, storage_key, metadata)) { + error_ = ModelError(FROM_HERE, "Failed to update entity metadata."); + } +} + +void AutofillMetadataChangeList::ClearMetadata(const std::string& storage_key) { + if (error_) { + return; + } + + if (!table_->ClearSyncMetadata(type_, storage_key)) { + error_ = ModelError(FROM_HERE, "Failed to clear entity metadata."); + } +} + +Optional<ModelError> AutofillMetadataChangeList::TakeError() { + Optional<ModelError> temp = error_; + error_.reset(); + return temp; +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/webdata/autofill_metadata_change_list.h b/chromium/components/autofill/core/browser/webdata/autofill_metadata_change_list.h new file mode 100644 index 00000000000..e924e2070a6 --- /dev/null +++ b/chromium/components/autofill/core/browser/webdata/autofill_metadata_change_list.h @@ -0,0 +1,55 @@ +// Copyright 2016 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_WEBDATA_AUTOFILL_METADATA_CHANGE_LIST_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_METADATA_CHANGE_LIST_H_ + +#include <string> + +#include "base/optional.h" +#include "components/autofill/core/browser/webdata/autofill_table.h" +#include "components/sync/base/model_type.h" +#include "components/sync/model/metadata_change_list.h" +#include "components/sync/model/model_error.h" +#include "components/sync/model/sync_error.h" +#include "components/sync/protocol/entity_metadata.pb.h" +#include "components/sync/protocol/model_type_state.pb.h" + +namespace autofill { + +// A thin wrapper around an AutofillTable that implements sync's +// MetadataChangeList interface. Changes are passed directly into the table and +// not stored inside this object. Since the table calls can fail, |TakeError()| +// must be called before this object is destroyed to check whether any +// operations failed. +class AutofillMetadataChangeList : public syncer::MetadataChangeList { + public: + AutofillMetadataChangeList(AutofillTable* table, syncer::ModelType type); + ~AutofillMetadataChangeList() override; + + // syncer::MetadataChangeList implementation. + void UpdateModelTypeState( + const sync_pb::ModelTypeState& model_type_state) override; + void ClearModelTypeState() override; + void UpdateMetadata(const std::string& storage_key, + const sync_pb::EntityMetadata& metadata) override; + void ClearMetadata(const std::string& storage_key) override; + + // Returns the value of |error_| and unsets it. + base::Optional<syncer::ModelError> TakeError(); + + private: + // The autofill table to store metadata in; always outlives |this|. + AutofillTable* table_; + + // The sync model type for this metadata. + syncer::ModelType type_; + + // The first error encountered by this object, if any. + base::Optional<syncer::ModelError> error_; +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_METADATA_CHANGE_LIST_H_ diff --git a/chromium/components/autofill/core/browser/webdata/autofill_profile_data_type_controller.cc b/chromium/components/autofill/core/browser/webdata/autofill_profile_data_type_controller.cc index 9d0f57057d8..f883c970e26 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_profile_data_type_controller.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_profile_data_type_controller.cc @@ -4,6 +4,8 @@ #include "components/autofill/core/browser/webdata/autofill_profile_data_type_controller.h" +#include <utility> + #include "base/bind.h" #include "base/metrics/histogram.h" #include "components/autofill/core/browser/personal_data_manager.h" @@ -17,23 +19,19 @@ using autofill::AutofillWebDataService; namespace browser_sync { AutofillProfileDataTypeController::AutofillProfileDataTypeController( - const scoped_refptr<base::SingleThreadTaskRunner>& db_thread, + scoped_refptr<base::SingleThreadTaskRunner> db_thread, const base::Closure& dump_stack, syncer::SyncClient* sync_client, const scoped_refptr<autofill::AutofillWebDataService>& web_data_service) - : NonUIDataTypeController(syncer::AUTOFILL_PROFILE, - dump_stack, - sync_client), - db_thread_(db_thread), + : AsyncDirectoryTypeController(syncer::AUTOFILL_PROFILE, + dump_stack, + sync_client, + syncer::GROUP_DB, + std::move(db_thread)), sync_client_(sync_client), web_data_service_(web_data_service), callback_registered_(false) {} -syncer::ModelSafeGroup AutofillProfileDataTypeController::model_safe_group() - const { - return syncer::GROUP_DB; -} - void AutofillProfileDataTypeController::WebDatabaseLoaded() { DCHECK(CalledOnValidThread()); OnModelLoaded(); @@ -60,13 +58,6 @@ void AutofillProfileDataTypeController::OnPersonalDataChanged() { AutofillProfileDataTypeController::~AutofillProfileDataTypeController() {} -bool AutofillProfileDataTypeController::PostTaskOnBackendThread( - const tracked_objects::Location& from_here, - const base::Closure& task) { - DCHECK(CalledOnValidThread()); - return db_thread_->PostTask(from_here, task); -} - bool AutofillProfileDataTypeController::StartModels() { DCHECK(CalledOnValidThread()); DCHECK_EQ(state(), MODEL_STARTING); diff --git a/chromium/components/autofill/core/browser/webdata/autofill_profile_data_type_controller.h b/chromium/components/autofill/core/browser/webdata/autofill_profile_data_type_controller.h index cf68f4af67d..6dd5fd55893 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_profile_data_type_controller.h +++ b/chromium/components/autofill/core/browser/webdata/autofill_profile_data_type_controller.h @@ -10,38 +10,32 @@ #include "base/memory/ref_counted.h" #include "base/scoped_observer.h" #include "components/autofill/core/browser/personal_data_manager_observer.h" -#include "components/sync/driver/non_ui_data_type_controller.h" +#include "components/sync/driver/async_directory_type_controller.h" namespace autofill { class AutofillWebDataService; -class PersonalDataManager; } // namespace autofill namespace browser_sync { // Controls syncing of the AUTOFILL_PROFILE data type. class AutofillProfileDataTypeController - : public syncer::NonUIDataTypeController, + : public syncer::AsyncDirectoryTypeController, public autofill::PersonalDataManagerObserver { public: // |dump_stack| is called when an unrecoverable error occurs. AutofillProfileDataTypeController( - const scoped_refptr<base::SingleThreadTaskRunner>& db_thread, + scoped_refptr<base::SingleThreadTaskRunner> db_thread, const base::Closure& dump_stack, syncer::SyncClient* sync_client, const scoped_refptr<autofill::AutofillWebDataService>& web_data_service); ~AutofillProfileDataTypeController() override; - // NonUIDataTypeController: - syncer::ModelSafeGroup model_safe_group() const override; - // PersonalDataManagerObserver: void OnPersonalDataChanged() override; protected: - // NonUIDataTypeController: - bool PostTaskOnBackendThread(const tracked_objects::Location& from_here, - const base::Closure& task) override; + // AsyncDirectoryTypeController: bool StartModels() override; void StopModels() override; @@ -49,9 +43,6 @@ class AutofillProfileDataTypeController // Callback to notify that WebDatabase has loaded. void WebDatabaseLoaded(); - // A reference to the DB thread's task runner. - const scoped_refptr<base::SingleThreadTaskRunner> db_thread_; - // A pointer to the sync client. syncer::SyncClient* const sync_client_; diff --git a/chromium/components/autofill/core/browser/webdata/autofill_table.cc b/chromium/components/autofill/core/browser/webdata/autofill_table.cc index 88867361266..503eb0fa9e7 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_table.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_table.cc @@ -35,6 +35,10 @@ #include "components/autofill/core/common/autofill_util.h" #include "components/autofill/core/common/form_field_data.h" #include "components/os_crypt/os_crypt.h" +#include "components/sync/base/model_type.h" +#include "components/sync/model/metadata_batch.h" +#include "components/sync/protocol/entity_metadata.pb.h" +#include "components/sync/protocol/model_type_state.pb.h" #include "components/webdata/common/web_database.h" #include "sql/statement.h" #include "sql/transaction.h" @@ -416,7 +420,8 @@ bool AutofillTable::CreateTablesIfNecessary() { InitProfilePhonesTable() && InitProfileTrashTable() && InitMaskedCreditCardsTable() && InitUnmaskedCreditCardsTable() && InitServerCardMetadataTable() && InitServerAddressesTable() && - InitServerAddressMetadataTable()); + InitServerAddressMetadataTable() && InitAutofillSyncMetadataTable() && + InitModelTypeStateTable()); } bool AutofillTable::IsSyncable() { @@ -463,6 +468,12 @@ bool AutofillTable::MigrateToVersion(int version, case 67: *update_compatible_version = false; return MigrateToVersion67AddMaskedCardBillingAddress(); + case 70: + *update_compatible_version = false; + return MigrateToVersion70AddSyncMetadata(); + case 71: + *update_compatible_version = true; + return MigrateToVersion71AddHasConvertedAndBillingAddressIdMetadata(); } return true; } @@ -934,7 +945,7 @@ bool AutofillTable::GetAutofillProfiles( } bool AutofillTable::GetServerProfiles( - std::vector<std::unique_ptr<AutofillProfile>>* profiles) { + std::vector<std::unique_ptr<AutofillProfile>>* profiles) const { profiles->clear(); sql::Statement s(db_->GetUniqueStatement( @@ -1197,22 +1208,22 @@ bool AutofillTable::GetCreditCards( } bool AutofillTable::GetServerCreditCards( - std::vector<std::unique_ptr<CreditCard>>* credit_cards) { + std::vector<std::unique_ptr<CreditCard>>* credit_cards) const { credit_cards->clear(); sql::Statement s(db_->GetUniqueStatement( "SELECT " - "card_number_encrypted, " // 0 - "last_four," // 1 - "masked.id," // 2 - "metadata.use_count," // 3 - "metadata.use_date," // 4 - "type," // 5 - "status," // 6 - "name_on_card," // 7 - "exp_month," // 8 - "exp_year," // 9 - "billing_address_id " // 10 + "card_number_encrypted, " // 0 + "last_four," // 1 + "masked.id," // 2 + "metadata.use_count," // 3 + "metadata.use_date," // 4 + "type," // 5 + "status," // 6 + "name_on_card," // 7 + "exp_month," // 8 + "exp_year," // 9 + "metadata.billing_address_id " // 10 "FROM masked_credit_cards masked " "LEFT OUTER JOIN unmasked_credit_cards USING (id) " "LEFT OUTER JOIN server_card_metadata metadata USING (id)")); @@ -1271,17 +1282,16 @@ void AutofillTable::SetServerCreditCards( "DELETE FROM masked_credit_cards")); masked_delete.Run(); - sql::Statement masked_insert(db_->GetUniqueStatement( - "INSERT INTO masked_credit_cards(" - "id," // 0 - "type," // 1 - "status," // 2 - "name_on_card," // 3 - "last_four," // 4 - "exp_month," // 5 - "exp_year," // 6 - "billing_address_id) " // 7 - "VALUES (?,?,?,?,?,?,?,?)")); + sql::Statement masked_insert( + db_->GetUniqueStatement("INSERT INTO masked_credit_cards(" + "id," // 0 + "type," // 1 + "status," // 2 + "name_on_card," // 3 + "last_four," // 4 + "exp_month," // 5 + "exp_year)" // 6 + "VALUES (?,?,?,?,?,?,?)")); for (const CreditCard& card : credit_cards) { DCHECK_EQ(CreditCard::MASKED_SERVER_CARD, card.record_type()); @@ -1294,13 +1304,12 @@ void AutofillTable::SetServerCreditCards( masked_insert.BindString16(5, card.GetRawInfo(CREDIT_CARD_EXP_MONTH)); masked_insert.BindString16(6, card.GetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR)); - masked_insert.BindString(7, card.billing_address_id()); masked_insert.Run(); masked_insert.Reset(true); // Save the use count and use date of the card. - UpdateServerCardUsageStats(card); + UpdateServerCardMetadata(card); } // Delete all items in the unmasked table that aren't in the new set. @@ -1341,7 +1350,7 @@ bool AutofillTable::UnmaskServerCreditCard(const CreditCard& masked, unmasked.set_record_type(CreditCard::FULL_SERVER_CARD); unmasked.SetNumber(full_number); unmasked.RecordAndLogUse(); - UpdateServerCardUsageStats(unmasked); + UpdateServerCardMetadata(unmasked); return db_->GetLastChangeCount() > 0; } @@ -1354,8 +1363,7 @@ bool AutofillTable::MaskServerCreditCard(const std::string& id) { return db_->GetLastChangeCount() > 0; } -bool AutofillTable::UpdateServerCardUsageStats( - const CreditCard& credit_card) { +bool AutofillTable::UpdateServerCardMetadata(const CreditCard& credit_card) { DCHECK_NE(CreditCard::LOCAL_CARD, credit_card.record_type()); sql::Transaction transaction(db_); if (!transaction.Begin()) @@ -1366,12 +1374,14 @@ bool AutofillTable::UpdateServerCardUsageStats( remove.BindString(0, credit_card.server_id()); remove.Run(); - sql::Statement s(db_->GetUniqueStatement( - "INSERT INTO server_card_metadata(use_count, use_date, id)" - "VALUES (?,?,?)")); + sql::Statement s( + db_->GetUniqueStatement("INSERT INTO server_card_metadata(use_count, " + "use_date, billing_address_id, id)" + "VALUES (?,?,?,?)")); s.BindInt64(0, credit_card.use_count()); s.BindInt64(1, credit_card.use_date().ToInternalValue()); - s.BindString(2, credit_card.server_id()); + s.BindString(2, credit_card.billing_address_id()); + s.BindString(3, credit_card.server_id()); s.Run(); transaction.Commit(); @@ -1379,7 +1389,9 @@ bool AutofillTable::UpdateServerCardUsageStats( return db_->GetLastChangeCount() > 0; } -bool AutofillTable::UpdateServerAddressUsageStats( +// TODO(crbug.com/680182): Record the address conversion status when a server +// address gets converted. +bool AutofillTable::UpdateServerAddressMetadata( const AutofillProfile& profile) { DCHECK_EQ(AutofillProfile::SERVER_PROFILE, profile.record_type()); @@ -1392,12 +1404,14 @@ bool AutofillTable::UpdateServerAddressUsageStats( remove.BindString(0, profile.server_id()); remove.Run(); - sql::Statement s(db_->GetUniqueStatement( - "INSERT INTO server_address_metadata(use_count, use_date, id)" - "VALUES (?,?,?)")); + sql::Statement s( + db_->GetUniqueStatement("INSERT INTO server_address_metadata(use_count, " + "use_date, has_converted, id)" + "VALUES (?,?,?,?)")); s.BindInt64(0, profile.use_count()); s.BindInt64(1, profile.use_date().ToInternalValue()); - s.BindString(2, profile.server_id()); + s.BindBool(2, false); + s.BindString(3, profile.server_id()); s.Run(); transaction.Commit(); @@ -1405,21 +1419,6 @@ bool AutofillTable::UpdateServerAddressUsageStats( return db_->GetLastChangeCount() > 0; } -bool AutofillTable::UpdateServerCardBillingAddress( - const CreditCard& credit_card) { - DCHECK_NE(CreditCard::LOCAL_CARD, credit_card.record_type()); - - sql::Statement update(db_->GetUniqueStatement( - "UPDATE masked_credit_cards SET billing_address_id = ? " - "WHERE id = ?")); - update.BindString(0, credit_card.billing_address_id()); - update.BindString(1, credit_card.server_id()); - if (!update.Run()) - return false; - - return db_->GetLastChangeCount() > 0; -} - bool AutofillTable::ClearAllServerData() { sql::Transaction transaction(db_); if (!transaction.Begin()) @@ -1680,6 +1679,122 @@ bool AutofillTable::IsAutofillGUIDInTrash(const std::string& guid) { return s.Step(); } +bool AutofillTable::GetAllSyncMetadata(syncer::ModelType model_type, + syncer::MetadataBatch* metadata_batch) { + DCHECK_EQ(model_type, syncer::AUTOFILL) + << "Only the AUTOFILL model type is supported"; + syncer::EntityMetadataMap metadata_records; + if (GetAllSyncEntityMetadata(model_type, &metadata_records)) { + for (const auto& pair : metadata_records) { + // todo(pnoland): add batch transfer of metadata map + metadata_batch->AddMetadata(pair.first, pair.second); + } + } else { + return false; + } + + sync_pb::ModelTypeState model_type_state; + if (GetModelTypeState(model_type, &model_type_state)) { + metadata_batch->SetModelTypeState(model_type_state); + } else { + return false; + } + + return true; +} + +bool AutofillTable::GetAllSyncEntityMetadata( + syncer::ModelType model_type, + syncer::EntityMetadataMap* metadata_records) { + DCHECK_EQ(model_type, syncer::AUTOFILL) + << "Only the AUTOFILL model type is supported"; + + sql::Statement s(db_->GetUniqueStatement( + "SELECT storage_key, value FROM autofill_sync_metadata")); + + while (s.Step()) { + std::string storage_key = s.ColumnString(0); + std::string serialized_metadata = s.ColumnString(1); + sync_pb::EntityMetadata metadata_record; + if (metadata_record.ParseFromString(serialized_metadata)) { + metadata_records->insert(std::make_pair(storage_key, metadata_record)); + } else { + return false; + } + } + return true; +} + +bool AutofillTable::UpdateSyncMetadata( + syncer::ModelType model_type, + const std::string& storage_key, + const sync_pb::EntityMetadata& metadata) { + DCHECK_EQ(model_type, syncer::AUTOFILL) + << "Only the AUTOFILL model type is supported"; + + sql::Statement s( + db_->GetUniqueStatement("INSERT OR REPLACE INTO autofill_sync_metadata " + "(storage_key, value) VALUES(?, ?)")); + s.BindString(0, storage_key); + s.BindString(1, metadata.SerializeAsString()); + + return s.Run(); +} + +bool AutofillTable::ClearSyncMetadata(syncer::ModelType model_type, + const std::string& storage_key) { + DCHECK_EQ(model_type, syncer::AUTOFILL) + << "Only the AUTOFILL model type is supported"; + + sql::Statement s(db_->GetUniqueStatement( + "DELETE FROM autofill_sync_metadata WHERE storage_key=?")); + s.BindString(0, storage_key); + + return s.Run(); +} + +bool AutofillTable::GetModelTypeState(syncer::ModelType model_type, + sync_pb::ModelTypeState* state) { + DCHECK_EQ(model_type, syncer::AUTOFILL) + << "Only the AUTOFILL model type is supported"; + + sql::Statement s(db_->GetUniqueStatement( + "SELECT value FROM autofill_model_type_state WHERE id=1")); + + if (!s.Step()) { + return false; + } + + std::string serialized_state = s.ColumnString(0); + return state->ParseFromString(serialized_state); +} + +bool AutofillTable::UpdateModelTypeState( + syncer::ModelType model_type, + const sync_pb::ModelTypeState& model_type_state) { + DCHECK_EQ(model_type, syncer::AUTOFILL) + << "Only the AUTOFILL model type is supported"; + + // Hardcode the id to force a collision, ensuring that there remains only a + // single entry. + sql::Statement s(db_->GetUniqueStatement( + "INSERT OR REPLACE INTO autofill_model_type_state (id, value) " + "VALUES(1,?)")); + s.BindString(0, model_type_state.SerializeAsString()); + + return s.Run(); +} + +bool AutofillTable::ClearModelTypeState(syncer::ModelType model_type) { + DCHECK_EQ(model_type, syncer::AUTOFILL) + << "Only the AUTOFILL model type is supported"; + + sql::Statement s(db_->GetUniqueStatement( + "DELETE FROM autofill_model_type_state WHERE id=1")); + + return s.Run(); +} + bool AutofillTable::InitMainTable() { if (!db_->DoesTableExist("autofill")) { if (!db_->Execute("CREATE TABLE autofill (" @@ -1804,8 +1919,7 @@ bool AutofillTable::InitMaskedCreditCardsTable() { "type VARCHAR," "last_four VARCHAR," "exp_month INTEGER DEFAULT 0," - "exp_year INTEGER DEFAULT 0, " - "billing_address_id VARCHAR)")) { + "exp_year INTEGER DEFAULT 0)")) { NOTREACHED(); return false; } @@ -1833,7 +1947,8 @@ bool AutofillTable::InitServerCardMetadataTable() { if (!db_->Execute("CREATE TABLE server_card_metadata (" "id VARCHAR NOT NULL," "use_count INTEGER NOT NULL DEFAULT 0, " - "use_date INTEGER NOT NULL DEFAULT 0)")) { + "use_date INTEGER NOT NULL DEFAULT 0, " + "billing_address_id VARCHAR)")) { NOTREACHED(); return false; } @@ -1871,7 +1986,31 @@ bool AutofillTable::InitServerAddressMetadataTable() { if (!db_->Execute("CREATE TABLE server_address_metadata (" "id VARCHAR NOT NULL," "use_count INTEGER NOT NULL DEFAULT 0, " - "use_date INTEGER NOT NULL DEFAULT 0)")) { + "use_date INTEGER NOT NULL DEFAULT 0, " + "has_converted BOOL NOT NULL DEFAULT FALSE)")) { + NOTREACHED(); + return false; + } + } + return true; +} + +bool AutofillTable::InitAutofillSyncMetadataTable() { + if (!db_->DoesTableExist("autofill_sync_metadata")) { + if (!db_->Execute("CREATE TABLE autofill_sync_metadata (" + "storage_key VARCHAR PRIMARY KEY NOT NULL," + "value BLOB)")) { + NOTREACHED(); + return false; + } + } + return true; +} + +bool AutofillTable::InitModelTypeStateTable() { + if (!db_->DoesTableExist("autofill_model_type_state")) { + if (!db_->Execute("CREATE TABLE autofill_model_type_state (id INTEGER " + "PRIMARY KEY, value BLOB)")) { NOTREACHED(); return false; } @@ -2315,4 +2454,77 @@ bool AutofillTable::MigrateToVersion67AddMaskedCardBillingAddress() { "ALTER TABLE masked_credit_cards ADD COLUMN billing_address_id VARCHAR"); } -} // namespace autofill +bool AutofillTable::MigrateToVersion70AddSyncMetadata() { + if (!db_->Execute("CREATE TABLE autofill_sync_metadata (" + "storage_key VARCHAR PRIMARY KEY NOT NULL," + "value BLOB)")) { + return false; + } + return db_->Execute( + "CREATE TABLE autofill_model_type_state (id INTEGER PRIMARY KEY, value " + "BLOB)"); +} + +bool AutofillTable:: + MigrateToVersion71AddHasConvertedAndBillingAddressIdMetadata() { + sql::Transaction transaction(db_); + if (!transaction.Begin()) + return false; + + // Add the new has_converted column to the server_address_metadata table. + if (!db_->DoesColumnExist("server_address_metadata", "has_converted") && + !db_->Execute("ALTER TABLE server_address_metadata ADD COLUMN " + "has_converted BOOL NOT NULL DEFAULT FALSE")) { + return false; + } + + // Add the new billing_address_id column to the server_card_metadata table. + if (!db_->DoesColumnExist("server_card_metadata", "billing_address_id") && + !db_->Execute("ALTER TABLE server_card_metadata ADD COLUMN " + "billing_address_id VARCHAR")) { + return false; + } + + // Copy over the billing_address_id from the masked_server_cards to + // server_card_metadata. + if (!db_->Execute("UPDATE server_card_metadata " + "SET billing_address_id = " + "(SELECT billing_address_id " + "FROM masked_credit_cards " + "WHERE id = server_card_metadata.id)")) { + return false; + } + + // Remove the billing_address_id column from the masked_credit_cards table. + // Create a temporary table that is a copy of masked_credit_cards but without + // the billing_address_id column. + if (db_->DoesTableExist("masked_credit_cards_temp") || + !db_->Execute("CREATE TABLE masked_credit_cards_temp (" + "id VARCHAR," + "status VARCHAR," + "name_on_card VARCHAR," + "type VARCHAR," + "last_four VARCHAR," + "exp_month INTEGER DEFAULT 0," + "exp_year INTEGER DEFAULT 0)")) { + return false; + } + // Copy over the data from the original masked_credit_cards table. + if (!db_->Execute("INSERT INTO masked_credit_cards_temp " + "SELECT id, status, name_on_card, type, last_four, " + "exp_month, exp_year " + "FROM masked_credit_cards")) { + return false; + } + // Delete the existing table and replace it with the contents of the + // temporary table. + if (!db_->Execute("DROP TABLE masked_credit_cards") || + !db_->Execute("ALTER TABLE masked_credit_cards_temp " + "RENAME TO masked_credit_cards")) { + return false; + } + + return transaction.Commit(); +} + +} // namespace autofill
\ No newline at end of file diff --git a/chromium/components/autofill/core/browser/webdata/autofill_table.h b/chromium/components/autofill/core/browser/webdata/autofill_table.h index a8eceb0ae1b..2f5fff15431 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_table.h +++ b/chromium/components/autofill/core/browser/webdata/autofill_table.h @@ -13,6 +13,8 @@ #include "base/gtest_prod_util.h" #include "base/macros.h" #include "base/strings/string16.h" +#include "components/sync/base/model_type.h" +#include "components/sync/model/metadata_batch.h" #include "components/webdata/common/web_database_table.h" class WebDatabase; @@ -21,6 +23,11 @@ namespace base { class Time; } +namespace sync_pb { +class EntityMetadata; +class ModelTypeState; +} + namespace autofill { class AutofillChange; @@ -160,10 +167,6 @@ struct FormFieldData; // with locally stored cards and generating descriptions. // exp_month Expiration month: 1-12 // exp_year Four-digit year: 2017 -// billing_address_id The guid string that identifies the local profile which -// is the billing address for this card. Can be null in the -// database, but always returned as an empty string in -// CreditCard. Added in version 67. // // unmasked_credit_cards // When a masked credit credit card is unmasked and the @@ -175,6 +178,7 @@ struct FormFieldData; // Full card number, encrypted. // use_count DEPRECATED in version 65. See server_card_metadata. // use_date DEPRECATED in version 65. See server_card_metadata. +// TODO(crbug.com/682326): Remove deprecated columns. // unmask_date The date this card was unmasked in units of // Time::ToInternalValue. Added in version 64. // @@ -188,6 +192,10 @@ struct FormFieldData; // a form. // use_date The date this card was last used to fill a form, // in internal t. +// billing_address_id The string that identifies the profile which is the +// billing address for this card. Can be null in the +// database, but always returned as an empty string in +// CreditCard. Added in version 71. // // server_addresses This table contains Autofill address data synced from // the wallet server. It's basically the same as the @@ -228,6 +236,21 @@ struct FormFieldData; // a form. // use_date The date this address was last used to fill a form, // in internal t. +// has_converted Whether this server address has been converted to a +// local autofill profile. +// +// autofill_sync_metadata +// Sync-specific metadata for autofill records. +// +// storage_key A string that uniquely identifies the metadata record +// as well as the corresponding autofill record. +// value The serialized EntityMetadata record. +// +// autofill_model_type_state +// Single row table that contains the sync ModelTypeState +// for the autofill model type. +// +// value The serialized ModelTypeState record. class AutofillTable : public WebDatabaseTable { public: @@ -320,7 +343,7 @@ class AutofillTable : public WebDatabaseTable { virtual bool GetAutofillProfiles( std::vector<std::unique_ptr<AutofillProfile>>* profiles); virtual bool GetServerProfiles( - std::vector<std::unique_ptr<AutofillProfile>>* profiles); + std::vector<std::unique_ptr<AutofillProfile>>* profiles) const; // Sets the server profiles. All old profiles are deleted and replaced with // the given ones. @@ -343,7 +366,7 @@ class AutofillTable : public WebDatabaseTable { virtual bool GetCreditCards( std::vector<std::unique_ptr<CreditCard>>* credit_cards); virtual bool GetServerCreditCards( - std::vector<std::unique_ptr<CreditCard>>* credit_cards); + std::vector<std::unique_ptr<CreditCard>>* credit_cards) const; // Replaces all server credit cards with the given vector. Unmasked cards // present in the new list will be preserved (even if the input is MASKED). @@ -356,10 +379,8 @@ class AutofillTable : public WebDatabaseTable { const base::string16& full_number); bool MaskServerCreditCard(const std::string& id); - bool UpdateServerCardUsageStats(const CreditCard& credit_card); - bool UpdateServerAddressUsageStats(const AutofillProfile& profile); - - bool UpdateServerCardBillingAddress(const CreditCard& credit_card); + bool UpdateServerCardMetadata(const CreditCard& credit_card); + bool UpdateServerAddressMetadata(const AutofillProfile& profile); // 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" @@ -403,6 +424,28 @@ class AutofillTable : public WebDatabaseTable { // Clear all profiles. bool ClearAutofillProfiles(); + // Read all the stored metadata for |model_type| and fill |metadata_batch| + // with it. + bool GetAllSyncMetadata(syncer::ModelType model_type, + syncer::MetadataBatch* metadata_batch); + + // Update the metadata row for |model_type|, keyed by |storage_key|, to + // contain the contents of |metadata|. + bool UpdateSyncMetadata(syncer::ModelType model_type, + const std::string& storage_key, + const sync_pb::EntityMetadata& metadata); + + // Remove the metadata row of type |model_type| keyed by |storage|key|. + bool ClearSyncMetadata(syncer::ModelType model_type, + const std::string& storage_key); + + // Update the stored sync state for the |model_type|. + bool UpdateModelTypeState(syncer::ModelType model_type, + const sync_pb::ModelTypeState& model_type_state); + + // Clear the stored sync state for |model_type|. + bool ClearModelTypeState(syncer::ModelType model_type); + // Table migration functions. NB: These do not and should not rely on other // functions in this class. The implementation of a function such as // GetCreditCard may change over time, but MigrateToVersionXX should never @@ -419,6 +462,8 @@ class AutofillTable : public WebDatabaseTable { bool MigrateToVersion65AddServerMetadataTables(); bool MigrateToVersion66AddCardBillingAddress(); bool MigrateToVersion67AddMaskedCardBillingAddress(); + bool MigrateToVersion70AddSyncMetadata(); + bool MigrateToVersion71AddHasConvertedAndBillingAddressIdMetadata(); // Max data length saved in the table, AKA the maximum length allowed for // form data. @@ -474,6 +519,12 @@ class AutofillTable : public WebDatabaseTable { std::vector<AutofillChange>* changes, base::Time time); + bool GetAllSyncEntityMetadata(syncer::ModelType model_type, + syncer::EntityMetadataMap* metadata_records); + + bool GetModelTypeState(syncer::ModelType model_type, + sync_pb::ModelTypeState* state); + // Insert a single AutofillEntry into the autofill table. bool InsertAutofillEntry(const AutofillEntry& entry); @@ -496,6 +547,8 @@ class AutofillTable : public WebDatabaseTable { bool InitServerCardMetadataTable(); bool InitServerAddressesTable(); bool InitServerAddressMetadataTable(); + bool InitAutofillSyncMetadataTable(); + bool InitModelTypeStateTable(); DISALLOW_COPY_AND_ASSIGN(AutofillTable); }; 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 18277b1f121..e8da2b12843 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc @@ -35,6 +35,8 @@ #include "components/autofill/core/common/autofill_util.h" #include "components/autofill/core/common/form_field_data.h" #include "components/os_crypt/os_crypt_mocker.h" +#include "components/sync/protocol/entity_metadata.pb.h" +#include "components/sync/protocol/model_type_state.pb.h" #include "components/webdata/common/web_database.h" #include "sql/statement.h" #include "testing/gtest/include/gtest/gtest.h" @@ -1716,7 +1718,7 @@ TEST_F(AutofillTableTest, SetServerCardModify) { outputs.clear(); } -TEST_F(AutofillTableTest, SetServerCardUpdateUsageStats) { +TEST_F(AutofillTableTest, SetServerCardUpdateUsageStatsAndBillingAddress) { // Add a masked card. CreditCard masked_card(CreditCard::MASKED_SERVER_CARD, "a123"); masked_card.SetRawInfo(CREDIT_CARD_NAME_FULL, @@ -1724,6 +1726,7 @@ TEST_F(AutofillTableTest, SetServerCardUpdateUsageStats) { masked_card.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("1")); masked_card.SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, ASCIIToUTF16("2020")); masked_card.SetRawInfo(CREDIT_CARD_NUMBER, ASCIIToUTF16("1111")); + masked_card.set_billing_address_id("1"); masked_card.SetTypeForMaskedCard(kVisaCard); std::vector<CreditCard> inputs; @@ -1744,13 +1747,15 @@ TEST_F(AutofillTableTest, SetServerCardUpdateUsageStats) { // Update the usage stats; make sure they're reflected in GetServerProfiles. inputs.back().set_use_count(4U); inputs.back().set_use_date(base::Time()); - table_->UpdateServerCardUsageStats(inputs.back()); + inputs.back().set_billing_address_id("2"); + table_->UpdateServerCardMetadata(inputs.back()); table_->GetServerCreditCards(&outputs); ASSERT_EQ(1u, outputs.size()); EXPECT_EQ(masked_card.server_id(), outputs[0]->server_id()); EXPECT_EQ(4U, outputs[0]->use_count()); EXPECT_EQ(base::Time(), outputs[0]->use_date()); EXPECT_EQ(base::Time(), outputs[0]->modification_date()); + EXPECT_EQ("2", outputs[0]->billing_address_id()); outputs.clear(); // Setting the cards again shouldn't delete the usage stats. @@ -1761,6 +1766,7 @@ TEST_F(AutofillTableTest, SetServerCardUpdateUsageStats) { EXPECT_EQ(4U, outputs[0]->use_count()); EXPECT_EQ(base::Time(), outputs[0]->use_date()); EXPECT_EQ(base::Time(), outputs[0]->modification_date()); + EXPECT_EQ("2", outputs[0]->billing_address_id()); outputs.clear(); // Set a card list where the card is missing --- this should clear metadata. @@ -1777,36 +1783,10 @@ TEST_F(AutofillTableTest, SetServerCardUpdateUsageStats) { EXPECT_EQ(1U, outputs[0]->use_count()); EXPECT_NE(base::Time(), outputs[0]->use_date()); EXPECT_EQ(base::Time(), outputs[0]->modification_date()); + EXPECT_EQ("1", outputs[0]->billing_address_id()); outputs.clear(); } -TEST_F(AutofillTableTest, UpdateServerCardBillingAddress) { - // Add a masked card. - CreditCard masked_card(CreditCard::MASKED_SERVER_CARD, "a123"); - masked_card.SetRawInfo(CREDIT_CARD_NAME_FULL, - ASCIIToUTF16("Paul F. Tompkins")); - masked_card.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("1")); - masked_card.SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, ASCIIToUTF16("2020")); - masked_card.SetRawInfo(CREDIT_CARD_NUMBER, ASCIIToUTF16("1111")); - masked_card.set_billing_address_id("billing-address-id-1"); - masked_card.SetTypeForMaskedCard(kVisaCard); - test::SetServerCreditCards(table_.get(), - std::vector<CreditCard>(1, masked_card)); - std::vector<std::unique_ptr<CreditCard>> outputs; - table_->GetServerCreditCards(&outputs); - ASSERT_EQ(1u, outputs.size()); - - EXPECT_EQ("billing-address-id-1", outputs[0]->billing_address_id()); - - masked_card.set_billing_address_id("billing-address-id-2"); - table_->UpdateServerCardBillingAddress(masked_card); - outputs.clear(); - table_->GetServerCreditCards(&outputs); - ASSERT_EQ(1u, outputs.size()); - - EXPECT_EQ("billing-address-id-2", outputs[0]->billing_address_id()); -} - TEST_F(AutofillTableTest, SetServerProfile) { AutofillProfile one(AutofillProfile::SERVER_PROFILE, "a123"); std::vector<AutofillProfile> inputs; @@ -1853,7 +1833,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()); - table_->UpdateServerAddressUsageStats(inputs.back()); + table_->UpdateServerAddressMetadata(inputs.back()); table_->GetServerProfiles(&outputs); ASSERT_EQ(1u, outputs.size()); EXPECT_EQ(one.server_id(), outputs[0]->server_id()); @@ -2016,4 +1996,90 @@ TEST_F(AutofillTableTest, GetFormValuesForElementName_SubstringMatchEnabled) { } } +TEST_F(AutofillTableTest, GetAllSyncMetadata) { + sync_pb::EntityMetadata metadata; + std::string storage_key = "storage_key"; + std::string storage_key2 = "storage_key2"; + metadata.set_sequence_number(1); + + EXPECT_TRUE( + table_->UpdateSyncMetadata(syncer::AUTOFILL, storage_key, metadata)); + + sync_pb::ModelTypeState model_type_state; + model_type_state.set_initial_sync_done(true); + + EXPECT_TRUE(table_->UpdateModelTypeState(syncer::AUTOFILL, model_type_state)); + + metadata.set_sequence_number(2); + EXPECT_TRUE( + table_->UpdateSyncMetadata(syncer::AUTOFILL, storage_key2, metadata)); + + syncer::MetadataBatch metadata_batch; + EXPECT_TRUE(table_->GetAllSyncMetadata(syncer::AUTOFILL, &metadata_batch)); + + EXPECT_TRUE(metadata_batch.GetModelTypeState().initial_sync_done()); + + syncer::EntityMetadataMap metadata_records = metadata_batch.TakeAllMetadata(); + + EXPECT_EQ(metadata_records.size(), 2u); + EXPECT_EQ(metadata_records[storage_key].sequence_number(), 1); + EXPECT_EQ(metadata_records[storage_key2].sequence_number(), 2); + + // Now check that a model type state update replaces the old value + model_type_state.set_initial_sync_done(false); + EXPECT_TRUE(table_->UpdateModelTypeState(syncer::AUTOFILL, model_type_state)); + + EXPECT_TRUE(table_->GetAllSyncMetadata(syncer::AUTOFILL, &metadata_batch)); + EXPECT_FALSE(metadata_batch.GetModelTypeState().initial_sync_done()); +} + +TEST_F(AutofillTableTest, WriteThenDeleteSyncMetadata) { + sync_pb::EntityMetadata metadata; + syncer::MetadataBatch metadata_batch; + std::string storage_key = "storage_key"; + sync_pb::ModelTypeState model_type_state; + + model_type_state.set_initial_sync_done(true); + + metadata.set_client_tag_hash("client_hash"); + + // Write the data into the store. + EXPECT_TRUE( + table_->UpdateSyncMetadata(syncer::AUTOFILL, storage_key, metadata)); + EXPECT_TRUE(table_->UpdateModelTypeState(syncer::AUTOFILL, model_type_state)); + // Delete the data we just wrote. + EXPECT_TRUE(table_->ClearSyncMetadata(syncer::AUTOFILL, storage_key)); + // It shouldn't be there any more. + EXPECT_TRUE(table_->GetAllSyncMetadata(syncer::AUTOFILL, &metadata_batch)); + + syncer::EntityMetadataMap metadata_records = metadata_batch.TakeAllMetadata(); + EXPECT_EQ(metadata_records.size(), 0u); + + // Now delete the model type state. + EXPECT_TRUE(table_->ClearModelTypeState(syncer::AUTOFILL)); + EXPECT_FALSE(table_->GetAllSyncMetadata(syncer::AUTOFILL, &metadata_batch)); +} + +TEST_F(AutofillTableTest, CorruptSyncMetadata) { + syncer::MetadataBatch metadata_batch; + sync_pb::ModelTypeState state; + std::string storage_key = "storage_key"; + + sql::Statement s(db_->GetSQLConnection()->GetUniqueStatement( + "INSERT OR REPLACE INTO autofill_sync_metadata " + "(storage_key, value) VALUES(?, ?)")); + s.BindString(0, storage_key); + s.BindString(1, "unparseable"); + + sql::Statement s2(db_->GetSQLConnection()->GetUniqueStatement( + "INSERT OR REPLACE INTO autofill_model_type_state " + "(rowid, value) VALUES(1, ?)")); + s2.BindString(0, "unparseable"); + + EXPECT_TRUE(s.Run()); + EXPECT_TRUE(s2.Run()); + + EXPECT_FALSE(table_->GetAllSyncMetadata(syncer::AUTOFILL, &metadata_batch)); +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.cc b/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.cc index 3706d27701e..326ddc4ee15 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.cc @@ -10,7 +10,6 @@ #include "base/base64.h" #include "base/bind.h" -#include "base/containers/scoped_ptr_hash_map.h" #include "base/location.h" #include "base/logging.h" #include "base/memory/ptr_util.h" @@ -41,29 +40,42 @@ void* UserDataKey() { return reinterpret_cast<void*>(&user_data_key); } -// Returns syncable metadata for the |local| profile or credit card. +// Sets the common syncable |metadata| for the |local_data_model|. +void SetCommonMetadata(sync_pb::WalletMetadataSpecifics::Type type, + const std::string& server_id, + const AutofillDataModel& local_data_model, + sync_pb::WalletMetadataSpecifics* metadata) { + metadata->set_type(type); + metadata->set_id(server_id); + metadata->set_use_count(local_data_model.use_count()); + metadata->set_use_date(local_data_model.use_date().ToInternalValue()); +} + +// Returns syncable metadata for the |local_profile|. syncer::SyncData BuildSyncData(sync_pb::WalletMetadataSpecifics::Type type, const std::string& server_id, - const AutofillDataModel& local) { + const AutofillProfile& local_profile) { sync_pb::EntitySpecifics entity; sync_pb::WalletMetadataSpecifics* metadata = entity.mutable_wallet_metadata(); - metadata->set_type(type); - metadata->set_id(server_id); - metadata->set_use_count(local.use_count()); - metadata->set_use_date(local.use_date().ToInternalValue()); - - std::string sync_tag; - switch (type) { - case sync_pb::WalletMetadataSpecifics::ADDRESS: - sync_tag = "address-" + server_id; - break; - case sync_pb::WalletMetadataSpecifics::CARD: - sync_tag = "card-" + server_id; - break; - case sync_pb::WalletMetadataSpecifics::UNKNOWN: - NOTREACHED(); - break; - } + SetCommonMetadata(type, server_id, local_profile, metadata); + metadata->set_address_has_converted(local_profile.has_converted()); + std::string sync_tag = "address-" + server_id; + + return syncer::SyncData::CreateLocalData(sync_tag, sync_tag, entity); +} + +// Returns syncable metadata for the |local_card|. +syncer::SyncData BuildSyncData(sync_pb::WalletMetadataSpecifics::Type type, + const std::string& server_id, + const CreditCard& local_card) { + sync_pb::EntitySpecifics entity; + sync_pb::WalletMetadataSpecifics* metadata = entity.mutable_wallet_metadata(); + SetCommonMetadata(type, server_id, local_card, metadata); + // The strings must be in valid UTF-8 to sync. + std::string billing_address_id; + base::Base64Encode(local_card.billing_address_id(), &billing_address_id); + metadata->set_card_billing_address_id(billing_address_id); + std::string sync_tag = "card-" + server_id; return syncer::SyncData::CreateLocalData(sync_tag, sync_tag, entity); } @@ -73,11 +85,12 @@ template <class DataType> void UndeleteMetadataIfExisting( const std::string& server_id, const sync_pb::WalletMetadataSpecifics::Type& metadata_type, - base::ScopedPtrHashMap<std::string, std::unique_ptr<DataType>>* locals, + std::unordered_map<std::string, std::unique_ptr<DataType>>* locals, syncer::SyncChangeList* changes_to_sync) { const auto& it = locals->find(server_id); if (it != locals->end()) { - std::unique_ptr<DataType> local_metadata = locals->take_and_erase(it); + std::unique_ptr<DataType> local_metadata = std::move(it->second); + locals->erase(it); changes_to_sync->push_back(syncer::SyncChange( FROM_HERE, syncer::SyncChange::ACTION_ADD, BuildSyncData(metadata_type, server_id, *local_metadata))); @@ -142,6 +155,89 @@ void ApplyChangesToCache(const syncer::SyncChangeList& changes, } } +// Merges the metadata of the remote and local versions of the data model. +void MergeCommonMetadata( + const sync_pb::WalletMetadataSpecifics& remote_metadata, + AutofillDataModel* local_model, + bool* is_remote_outdated, + bool* is_local_modified) { + size_t remote_use_count = + base::checked_cast<size_t>(remote_metadata.use_count()); + if (local_model->use_count() < remote_use_count) { + local_model->set_use_count(remote_use_count); + *is_local_modified = true; + } else if (local_model->use_count() > remote_use_count) { + *is_remote_outdated = true; + } + + base::Time remote_use_date = + base::Time::FromInternalValue(remote_metadata.use_date()); + if (local_model->use_date() < remote_use_date) { + local_model->set_use_date(remote_use_date); + *is_local_modified = true; + } else if (local_model->use_date() > remote_use_date) { + *is_remote_outdated = true; + } +} + +// Merges the metadata of the remote and local versions of the profile. +void MergeMetadata(const sync_pb::WalletMetadataSpecifics& remote_metadata, + AutofillProfile* local_profile, + bool* is_remote_outdated, + bool* is_local_modified) { + // Merge the has_converted status. + if (local_profile->has_converted() != + remote_metadata.address_has_converted()) { + if (!local_profile->has_converted()) { + local_profile->set_has_converted(true); + *is_local_modified = true; + } else { + *is_remote_outdated = true; + } + } + + // Merge the use_count and use_date. + MergeCommonMetadata(remote_metadata, local_profile, is_remote_outdated, + is_local_modified); +} + +// Merges the metadata of the remote and local versions of the credit card. +void MergeMetadata(const sync_pb::WalletMetadataSpecifics& remote_metadata, + CreditCard* local_card, + bool* is_remote_outdated, + bool* is_local_modified) { + // Merge the billing_address_id. Do this before updating the use_count + // because it may be used to determine what id to keep. + std::string remote_billing_address_id; + base::Base64Decode(remote_metadata.card_billing_address_id(), + &remote_billing_address_id); + + if (local_card->billing_address_id() != remote_billing_address_id) { + // If one of the values is empty, update it with the non empty value. + if (local_card->billing_address_id().empty()) { + local_card->set_billing_address_id(remote_billing_address_id); + *is_local_modified = true; + } else if (remote_billing_address_id.empty()) { + *is_remote_outdated = true; + } else { + // The cards have a different non-empty billing address id. Keep the + // billing address id of the most recently used card. + base::Time remote_use_date = + base::Time::FromInternalValue(remote_metadata.use_date()); + if (local_card->use_date() < remote_use_date) { + local_card->set_billing_address_id(remote_billing_address_id); + *is_local_modified = true; + } else { + *is_remote_outdated = true; + } + } + } + + // Merge the use_count and use_date. + MergeCommonMetadata(remote_metadata, local_card, is_remote_outdated, + is_local_modified); +} + // Merges |remote| metadata into a collection of metadata |locals|. Returns true // if the corresponding local metadata was found. // @@ -151,7 +247,7 @@ template <class DataType> bool MergeRemote( const syncer::SyncData& remote, const base::Callback<bool(const DataType&)>& updater, - base::ScopedPtrHashMap<std::string, std::unique_ptr<DataType>>* locals, + std::unordered_map<std::string, std::unique_ptr<DataType>>* locals, syncer::SyncChangeList* changes_to_sync) { DCHECK(locals); DCHECK(changes_to_sync); @@ -162,27 +258,13 @@ bool MergeRemote( if (it == locals->end()) return false; - std::unique_ptr<DataType> local_metadata = locals->take_and_erase(it); + std::unique_ptr<DataType> local_metadata = std::move(it->second); + locals->erase(it); - size_t remote_use_count = - base::checked_cast<size_t>(remote_metadata.use_count()); bool is_local_modified = false; bool is_remote_outdated = false; - if (local_metadata->use_count() < remote_use_count) { - local_metadata->set_use_count(remote_use_count); - is_local_modified = true; - } else if (local_metadata->use_count() > remote_use_count) { - is_remote_outdated = true; - } - - base::Time remote_use_date = - base::Time::FromInternalValue(remote_metadata.use_date()); - if (local_metadata->use_date() < remote_use_date) { - local_metadata->set_use_date(remote_use_date); - is_local_modified = true; - } else if (local_metadata->use_date() > remote_use_date) { - is_remote_outdated = true; - } + MergeMetadata(remote_metadata, local_metadata.get(), &is_remote_outdated, + &is_local_modified); if (is_remote_outdated) { changes_to_sync->push_back(syncer::SyncChange( @@ -247,9 +329,8 @@ syncer::SyncDataList AutofillWalletMetadataSyncableService::GetAllSyncData( DCHECK_EQ(syncer::AUTOFILL_WALLET_METADATA, type); syncer::SyncDataList data_list; - base::ScopedPtrHashMap<std::string, std::unique_ptr<AutofillProfile>> - profiles; - base::ScopedPtrHashMap<std::string, std::unique_ptr<CreditCard>> cards; + std::unordered_map<std::string, std::unique_ptr<AutofillProfile>> profiles; + std::unordered_map<std::string, std::unique_ptr<CreditCard>> cards; if (GetLocalData(&profiles, &cards)) { for (const auto& it : profiles) { data_list.push_back(BuildSyncData( @@ -272,9 +353,8 @@ syncer::SyncError AutofillWalletMetadataSyncableService::ProcessSyncChanges( ApplyChangesToCache(changes_from_sync, &cache_); - base::ScopedPtrHashMap<std::string, std::unique_ptr<AutofillProfile>> - profiles; - base::ScopedPtrHashMap<std::string, std::unique_ptr<CreditCard>> cards; + std::unordered_map<std::string, std::unique_ptr<AutofillProfile>> profiles; + std::unordered_map<std::string, std::unique_ptr<CreditCard>> cards; GetLocalData(&profiles, &cards); // base::Unretained is used because the callbacks are invoked synchronously. @@ -399,17 +479,15 @@ AutofillWalletMetadataSyncableService::AutofillWalletMetadataSyncableService( } bool AutofillWalletMetadataSyncableService::GetLocalData( - base::ScopedPtrHashMap<std::string, std::unique_ptr<AutofillProfile>>* - profiles, - base::ScopedPtrHashMap<std::string, std::unique_ptr<CreditCard>>* cards) - const { + std::unordered_map<std::string, std::unique_ptr<AutofillProfile>>* profiles, + std::unordered_map<std::string, std::unique_ptr<CreditCard>>* cards) const { std::vector<std::unique_ptr<AutofillProfile>> profile_list; bool success = AutofillTable::FromWebDatabase(web_data_backend_->GetDatabase()) ->GetServerProfiles(&profile_list); while (!profile_list.empty()) { auto server_id = GetServerId(*profile_list.front()); - profiles->add(server_id, std::move(profile_list.front())); + (*profiles)[server_id] = std::move(profile_list.front()); profile_list.erase(profile_list.begin()); } @@ -418,7 +496,7 @@ bool AutofillWalletMetadataSyncableService::GetLocalData( ->GetServerCreditCards(&card_list); while (!card_list.empty()) { auto server_id = GetServerId(*card_list.front()); - cards->add(server_id, std::move(card_list.front())); + (*cards)[server_id] = std::move(card_list.front()); card_list.erase(card_list.begin()); } @@ -428,13 +506,13 @@ bool AutofillWalletMetadataSyncableService::GetLocalData( bool AutofillWalletMetadataSyncableService::UpdateAddressStats( const AutofillProfile& profile) { return AutofillTable::FromWebDatabase(web_data_backend_->GetDatabase()) - ->UpdateServerAddressUsageStats(profile); + ->UpdateServerAddressMetadata(profile); } bool AutofillWalletMetadataSyncableService::UpdateCardStats( const CreditCard& credit_card) { return AutofillTable::FromWebDatabase(web_data_backend_->GetDatabase()) - ->UpdateServerCardUsageStats(credit_card); + ->UpdateServerCardMetadata(credit_card); } syncer::SyncError @@ -447,9 +525,8 @@ AutofillWalletMetadataSyncableService::SendChangesToSyncServer( syncer::SyncMergeResult AutofillWalletMetadataSyncableService::MergeData( const syncer::SyncDataList& sync_data) { - base::ScopedPtrHashMap<std::string, std::unique_ptr<AutofillProfile>> - profiles; - base::ScopedPtrHashMap<std::string, std::unique_ptr<CreditCard>> cards; + std::unordered_map<std::string, std::unique_ptr<AutofillProfile>> profiles; + std::unordered_map<std::string, std::unique_ptr<CreditCard>> cards; GetLocalData(&profiles, &cards); syncer::SyncMergeResult result(syncer::AUTOFILL_WALLET_METADATA); @@ -518,16 +595,18 @@ syncer::SyncMergeResult AutofillWalletMetadataSyncableService::MergeData( return result; } +template <class DataType> void AutofillWalletMetadataSyncableService::AutofillDataModelChanged( const std::string& server_id, const sync_pb::WalletMetadataSpecifics::Type& type, - const AutofillDataModel& local) { + const DataType& local) { auto it = FindServerIdAndTypeInCache(server_id, type, &cache_); if (it == cache_.end()) return; const sync_pb::WalletMetadataSpecifics& remote = it->GetSpecifics().wallet_metadata(); + if (base::checked_cast<size_t>(remote.use_count()) < local.use_count() && base::Time::FromInternalValue(remote.use_date()) < local.use_date()) { SendChangesToSyncServer(syncer::SyncChangeList( diff --git a/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.h b/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.h index 4987768f7e7..23edced9e32 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.h +++ b/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.h @@ -7,6 +7,7 @@ #include <memory> #include <string> +#include <unordered_map> #include "base/callback_forward.h" #include "base/macros.h" @@ -20,14 +21,8 @@ #include "components/sync/model/syncable_service.h" #include "components/sync/protocol/autofill_specifics.pb.h" -namespace base { -template <typename, typename> -class ScopedPtrHashMap; -} - namespace syncer { class SyncChangeProcessor; -class SyncData; class SyncErrorFactory; } @@ -37,7 +32,6 @@ class Location; namespace autofill { -class AutofillDataModel; class AutofillProfile; class AutofillWebDataBackend; class AutofillWebDataService; @@ -97,9 +91,9 @@ class AutofillWalletMetadataSyncableService // to server profiles and server cards read from disk. This data contains the // usage stats. Returns true on success. virtual bool GetLocalData( - base::ScopedPtrHashMap<std::string, std::unique_ptr<AutofillProfile>>* + std::unordered_map<std::string, std::unique_ptr<AutofillProfile>>* profiles, - base::ScopedPtrHashMap<std::string, std::unique_ptr<CreditCard>>* cards) + std::unordered_map<std::string, std::unique_ptr<CreditCard>>* cards) const; // Updates the stats for |profile| stored on disk. Does not trigger @@ -128,11 +122,13 @@ class AutofillWalletMetadataSyncableService // is not present locally. syncer::SyncMergeResult MergeData(const syncer::SyncDataList& sync_data); - // Sends updates to the sync server. + // Sends the autofill data model updates to the sync server if the local + // version is more recent. Used for both profiles and credit cards. + template <class DataType> void AutofillDataModelChanged( const std::string& server_id, const sync_pb::WalletMetadataSpecifics::Type& type, - const AutofillDataModel& local); + const DataType& local); base::ThreadChecker thread_checker_; AutofillWebDataBackend* web_data_backend_; // Weak ref. diff --git a/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service_unittest.cc b/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service_unittest.cc index 70438328595..89439d7184c 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service_unittest.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service_unittest.cc @@ -11,7 +11,6 @@ #include <vector> #include "base/base64.h" -#include "base/containers/scoped_ptr_hash_map.h" #include "base/location.h" #include "base/macros.h" #include "base/memory/ptr_util.h" @@ -65,13 +64,13 @@ ACTION_P2(GetCopiesOf, profiles, cards) { for (const auto& profile : *profiles) { std::string utf8_server_id; base::Base64Encode(profile.server_id(), &utf8_server_id); - arg0->add(utf8_server_id, base::WrapUnique(new AutofillProfile(profile))); + (*arg0)[utf8_server_id] = base::MakeUnique<AutofillProfile>(profile); } for (const auto& card : *cards) { std::string utf8_server_id; base::Base64Encode(card.server_id(), &utf8_server_id); - arg1->add(utf8_server_id, base::WrapUnique(new CreditCard(card))); + (*arg1)[utf8_server_id] = base::MakeUnique<CreditCard>(card); } } @@ -121,9 +120,8 @@ class MockService : public AutofillWalletMetadataSyncableService { private: MOCK_CONST_METHOD2( GetLocalData, - bool(base::ScopedPtrHashMap<std::string, - std::unique_ptr<AutofillProfile>>*, - base::ScopedPtrHashMap<std::string, std::unique_ptr<CreditCard>>*)); + bool(std::unordered_map<std::string, std::unique_ptr<AutofillProfile>>*, + std::unordered_map<std::string, std::unique_ptr<CreditCard>>*)); syncer::SyncError SendChangesToSyncServerConcrete( const syncer::SyncChangeList& changes) { @@ -185,28 +183,33 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, NoMetadataToReturn) { AutofillProfile BuildAddress(const std::string& server_id, int64_t use_count, - int64_t use_date) { + int64_t use_date, + bool has_converted) { AutofillProfile profile(AutofillProfile::SERVER_PROFILE, server_id); profile.set_use_count(use_count); profile.set_use_date(base::Time::FromInternalValue(use_date)); + profile.set_has_converted(has_converted); return profile; } CreditCard BuildCard(const std::string& server_id, int64_t use_count, - int64_t use_date) { + int64_t use_date, + const std::string& billing_address_id) { CreditCard card(CreditCard::MASKED_SERVER_CARD, server_id); card.set_use_count(use_count); card.set_use_date(base::Time::FromInternalValue(use_date)); + card.set_billing_address_id(billing_address_id); return card; } -MATCHER_P5(SyncDataMatches, +MATCHER_P6(SyncAddressDataMatches, sync_tag, metadata_type, server_id, use_count, use_date, + has_converted, "") { return arg.IsValid() && syncer::AUTOFILL_WALLET_METADATA == arg.GetDataType() && @@ -214,22 +217,43 @@ MATCHER_P5(SyncDataMatches, metadata_type == arg.GetSpecifics().wallet_metadata().type() && server_id == arg.GetSpecifics().wallet_metadata().id() && use_count == arg.GetSpecifics().wallet_metadata().use_count() && - use_date == arg.GetSpecifics().wallet_metadata().use_date(); + use_date == arg.GetSpecifics().wallet_metadata().use_date() && + has_converted == + arg.GetSpecifics().wallet_metadata().address_has_converted(); +} + +MATCHER_P6(SyncCardDataMatches, + sync_tag, + metadata_type, + server_id, + use_count, + use_date, + billing_address_id, + "") { + return arg.IsValid() && + syncer::AUTOFILL_WALLET_METADATA == arg.GetDataType() && + sync_tag == syncer::SyncDataLocal(arg).GetTag() && + metadata_type == arg.GetSpecifics().wallet_metadata().type() && + server_id == arg.GetSpecifics().wallet_metadata().id() && + use_count == arg.GetSpecifics().wallet_metadata().use_count() && + use_date == arg.GetSpecifics().wallet_metadata().use_date() && + billing_address_id == + arg.GetSpecifics().wallet_metadata().card_billing_address_id(); } // Verify that all metadata from disk is sent to the sync server. TEST_F(AutofillWalletMetadataSyncableServiceTest, ReturnAllMetadata) { - local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - local_.UpdateCardStats(BuildCard(kCard1, 3, 4)); - - EXPECT_THAT( - local_.GetAllSyncData(syncer::AUTOFILL_WALLET_METADATA), - UnorderedElementsAre( - SyncDataMatches(kAddr1SyncTag, - sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, - 1, 2), - SyncDataMatches(kCard1SyncTag, sync_pb::WalletMetadataSpecifics::CARD, - kCard1Utf8, 3, 4))); + local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); + + EXPECT_THAT(local_.GetAllSyncData(syncer::AUTOFILL_WALLET_METADATA), + UnorderedElementsAre( + SyncAddressDataMatches( + kAddr1SyncTag, sync_pb::WalletMetadataSpecifics::ADDRESS, + kAddr1Utf8, 1, 2, true), + SyncCardDataMatches(kCard1SyncTag, + sync_pb::WalletMetadataSpecifics::CARD, + kCard1Utf8, 3, 4, kAddr1Utf8))); } void MergeMetadata(MockService* local, MockService* remote) { @@ -272,8 +296,8 @@ MATCHER_P2(SyncChangeMatches, change_type, sync_tag, "") { // Verify that remote data without local counterpart is deleted during the // initial merge. TEST_F(AutofillWalletMetadataSyncableServiceTest, DeleteFromServerOnMerge) { - remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - remote_.UpdateCardStats(BuildCard(kCard1, 3, 4)); + remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); EXPECT_CALL(local_, UpdateAddressStats(_)).Times(0); EXPECT_CALL(local_, UpdateCardStats(_)).Times(0); @@ -287,37 +311,53 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, DeleteFromServerOnMerge) { MergeMetadata(&local_, &remote_); } -MATCHER_P6(SyncChangeAndDataMatch, +MATCHER_P7(SyncAddressChangeAndDataMatch, + change_type, + sync_tag, + metadata_type, + server_id, + use_count, + use_date, + has_converted, + "") { + return Value(arg, SyncChangeMatches(change_type, sync_tag)) && + Value(arg.sync_data(), + SyncAddressDataMatches(sync_tag, metadata_type, server_id, + use_count, use_date, has_converted)); +} + +MATCHER_P7(SyncCardChangeAndDataMatch, change_type, sync_tag, metadata_type, server_id, use_count, use_date, + billing_address_id, "") { return Value(arg, SyncChangeMatches(change_type, sync_tag)) && Value(arg.sync_data(), - SyncDataMatches(sync_tag, metadata_type, server_id, use_count, - use_date)); + SyncCardDataMatches(sync_tag, metadata_type, server_id, + use_count, use_date, billing_address_id)); } // Verify that local data is sent to the sync server during the initial merge, // if the server does not have the data already. TEST_F(AutofillWalletMetadataSyncableServiceTest, AddToServerOnMerge) { - local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - local_.UpdateCardStats(BuildCard(kCard1, 3, 4)); + local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); EXPECT_CALL(local_, UpdateAddressStats(_)).Times(0); EXPECT_CALL(local_, UpdateCardStats(_)).Times(0); - EXPECT_CALL( - local_, - SendChangesToSyncServer(UnorderedElementsAre( - SyncChangeAndDataMatch(syncer::SyncChange::ACTION_ADD, kAddr1SyncTag, - sync_pb::WalletMetadataSpecifics::ADDRESS, - kAddr1Utf8, 1, 2), - SyncChangeAndDataMatch(syncer::SyncChange::ACTION_ADD, kCard1SyncTag, - sync_pb::WalletMetadataSpecifics::CARD, - kCard1Utf8, 3, 4)))); + EXPECT_CALL(local_, SendChangesToSyncServer(UnorderedElementsAre( + SyncAddressChangeAndDataMatch( + syncer::SyncChange::ACTION_ADD, kAddr1SyncTag, + sync_pb::WalletMetadataSpecifics::ADDRESS, + kAddr1Utf8, 1, 2, true), + SyncCardChangeAndDataMatch( + syncer::SyncChange::ACTION_ADD, kCard1SyncTag, + sync_pb::WalletMetadataSpecifics::CARD, + kCard1Utf8, 3, 4, kAddr1Utf8)))); MergeMetadata(&local_, &remote_); } @@ -326,10 +366,10 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, AddToServerOnMerge) { // local and remote data are identical during the initial merge. TEST_F(AutofillWalletMetadataSyncableServiceTest, IgnoreIdenticalValuesOnMerge) { - local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - local_.UpdateCardStats(BuildCard(kCard1, 3, 4)); - remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - remote_.UpdateCardStats(BuildCard(kCard1, 3, 4)); + local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); + remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); EXPECT_CALL(local_, UpdateAddressStats(_)).Times(0); EXPECT_CALL(local_, UpdateCardStats(_)).Times(0); @@ -338,24 +378,43 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, MergeMetadata(&local_, &remote_); } -MATCHER_P3(AutofillMetadataMatches, server_id, use_count, use_date, "") { +MATCHER_P4(AutofillAddressMetadataMatches, + server_id, + use_count, + use_date, + has_converted, + "") { + return arg.server_id() == server_id && + arg.use_count() == base::checked_cast<size_t>(use_count) && + arg.use_date() == base::Time::FromInternalValue(use_date) && + arg.has_converted() == has_converted; +} + +MATCHER_P4(AutofillCardMetadataMatches, + server_id, + use_count, + use_date, + billing_address_id, + "") { return arg.server_id() == server_id && arg.use_count() == base::checked_cast<size_t>(use_count) && - arg.use_date() == base::Time::FromInternalValue(use_date); + arg.use_date() == base::Time::FromInternalValue(use_date) && + arg.billing_address_id() == billing_address_id; } // Verify that remote data with higher values of use count and last use date is // saved to disk during the initial merge. TEST_F(AutofillWalletMetadataSyncableServiceTest, SaveHigherValuesLocallyOnMerge) { - local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - local_.UpdateCardStats(BuildCard(kCard1, 3, 4)); - remote_.UpdateAddressStats(BuildAddress(kAddr1, 10, 20)); - remote_.UpdateCardStats(BuildCard(kCard1, 30, 40)); - - EXPECT_CALL(local_, - UpdateAddressStats(AutofillMetadataMatches(kAddr1, 10, 20))); - EXPECT_CALL(local_, UpdateCardStats(AutofillMetadataMatches(kCard1, 30, 40))); + local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); + remote_.UpdateAddressStats(BuildAddress(kAddr1, 10, 20, true)); + remote_.UpdateCardStats(BuildCard(kCard1, 30, 40, kAddr1)); + + EXPECT_CALL(local_, UpdateAddressStats(AutofillAddressMetadataMatches( + kAddr1, 10, 20, true))); + EXPECT_CALL(local_, UpdateCardStats( + AutofillCardMetadataMatches(kCard1, 30, 40, kAddr1))); EXPECT_CALL(local_, SendChangesToSyncServer(_)).Times(0); MergeMetadata(&local_, &remote_); @@ -365,22 +424,22 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, // sent to the sync server during the initial merge. TEST_F(AutofillWalletMetadataSyncableServiceTest, SendHigherValuesToServerOnMerge) { - local_.UpdateAddressStats(BuildAddress(kAddr1, 10, 20)); - local_.UpdateCardStats(BuildCard(kCard1, 30, 40)); - remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - remote_.UpdateCardStats(BuildCard(kCard1, 3, 4)); + local_.UpdateAddressStats(BuildAddress(kAddr1, 10, 20, true)); + local_.UpdateCardStats(BuildCard(kCard1, 30, 40, kAddr1)); + remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, false)); + remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr2)); EXPECT_CALL(local_, UpdateAddressStats(_)).Times(0); EXPECT_CALL(local_, UpdateCardStats(_)).Times(0); - EXPECT_CALL( - local_, - SendChangesToSyncServer(UnorderedElementsAre( - SyncChangeAndDataMatch( - syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag, - sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 10, 20), - SyncChangeAndDataMatch( - syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag, - sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 30, 40)))); + EXPECT_CALL(local_, SendChangesToSyncServer(UnorderedElementsAre( + SyncAddressChangeAndDataMatch( + syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag, + sync_pb::WalletMetadataSpecifics::ADDRESS, + kAddr1Utf8, 10, 20, true), + SyncCardChangeAndDataMatch( + syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag, + sync_pb::WalletMetadataSpecifics::CARD, + kCard1Utf8, 30, 40, kAddr1Utf8)))); MergeMetadata(&local_, &remote_); } @@ -389,13 +448,13 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, // local metadata is updated. TEST_F(AutofillWalletMetadataSyncableServiceTest, DontSendLowerValueToServerOnSingleChange) { - local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - local_.UpdateCardStats(BuildCard(kCard1, 3, 4)); - remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - remote_.UpdateCardStats(BuildCard(kCard1, 3, 4)); + local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); + remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); MergeMetadata(&local_, &remote_); - AutofillProfile address = BuildAddress(kAddr1, 0, 0); - CreditCard card = BuildCard(kCard1, 0, 0); + AutofillProfile address = BuildAddress(kAddr1, 0, 0, false); + CreditCard card = BuildCard(kCard1, 0, 0, kAddr2); EXPECT_CALL(local_, SendChangesToSyncServer(_)).Times(0); @@ -409,24 +468,24 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, // metadata is updated. TEST_F(AutofillWalletMetadataSyncableServiceTest, SendHigherValuesToServerOnLocalSingleChange) { - local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - local_.UpdateCardStats(BuildCard(kCard1, 3, 4)); - remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - remote_.UpdateCardStats(BuildCard(kCard1, 3, 4)); + local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, false)); + local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); + remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, false)); + remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); MergeMetadata(&local_, &remote_); - AutofillProfile address = BuildAddress(kAddr1, 10, 20); - CreditCard card = BuildCard(kCard1, 30, 40); + AutofillProfile address = BuildAddress(kAddr1, 10, 20, true); + CreditCard card = BuildCard(kCard1, 30, 40, kAddr2); - EXPECT_CALL( - local_, - SendChangesToSyncServer(ElementsAre(SyncChangeAndDataMatch( - syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag, - sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 10, 20)))); - EXPECT_CALL( - local_, - SendChangesToSyncServer(ElementsAre(SyncChangeAndDataMatch( - syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag, - sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 30, 40)))); + EXPECT_CALL(local_, + SendChangesToSyncServer(ElementsAre(SyncAddressChangeAndDataMatch( + syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag, + sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 10, 20, + true)))); + EXPECT_CALL(local_, + SendChangesToSyncServer(ElementsAre(SyncCardChangeAndDataMatch( + syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag, + sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 30, 40, + kAddr2Utf8)))); local_.AutofillProfileChanged(AutofillProfileChange( AutofillProfileChange::UPDATE, address.guid(), &address)); @@ -439,13 +498,13 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, // instead. TEST_F(AutofillWalletMetadataSyncableServiceTest, DontAddToServerOnSingleChange) { - local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - local_.UpdateCardStats(BuildCard(kCard1, 3, 4)); - remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - remote_.UpdateCardStats(BuildCard(kCard1, 3, 4)); + local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); + remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); MergeMetadata(&local_, &remote_); - AutofillProfile address = BuildAddress(kAddr2, 5, 6); - CreditCard card = BuildCard(kCard2, 7, 8); + AutofillProfile address = BuildAddress(kAddr2, 5, 6, false); + CreditCard card = BuildCard(kCard2, 7, 8, kAddr2); EXPECT_CALL(local_, SendChangesToSyncServer(_)).Times(0); @@ -458,24 +517,24 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, // Verify that new metadata is sent to the sync server when multiple metadata // values change at once. TEST_F(AutofillWalletMetadataSyncableServiceTest, AddToServerOnMultiChange) { - local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - local_.UpdateCardStats(BuildCard(kCard1, 3, 4)); - remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - remote_.UpdateCardStats(BuildCard(kCard1, 3, 4)); + local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, false)); + local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); + remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, false)); + remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); MergeMetadata(&local_, &remote_); // These methods do not trigger notifications or sync: - local_.UpdateAddressStats(BuildAddress(kAddr2, 5, 6)); - local_.UpdateCardStats(BuildCard(kCard2, 7, 8)); - - EXPECT_CALL( - local_, - SendChangesToSyncServer(UnorderedElementsAre( - SyncChangeAndDataMatch(syncer::SyncChange::ACTION_ADD, kAddr2SyncTag, - sync_pb::WalletMetadataSpecifics::ADDRESS, - kAddr2Utf8, 5, 6), - SyncChangeAndDataMatch(syncer::SyncChange::ACTION_ADD, kCard2SyncTag, - sync_pb::WalletMetadataSpecifics::CARD, - kCard2Utf8, 7, 8)))); + local_.UpdateAddressStats(BuildAddress(kAddr2, 5, 6, true)); + local_.UpdateCardStats(BuildCard(kCard2, 7, 8, kAddr2)); + + EXPECT_CALL(local_, SendChangesToSyncServer(UnorderedElementsAre( + SyncAddressChangeAndDataMatch( + syncer::SyncChange::ACTION_ADD, kAddr2SyncTag, + sync_pb::WalletMetadataSpecifics::ADDRESS, + kAddr2Utf8, 5, 6, true), + SyncCardChangeAndDataMatch( + syncer::SyncChange::ACTION_ADD, kCard2SyncTag, + sync_pb::WalletMetadataSpecifics::CARD, + kCard2Utf8, 7, 8, kAddr2Utf8)))); local_.AutofillMultipleChanged(); } @@ -484,24 +543,24 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, AddToServerOnMultiChange) { // when multiple metadata values change at once. TEST_F(AutofillWalletMetadataSyncableServiceTest, UpdateToHigherValueOnServerOnMultiChange) { - local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - local_.UpdateCardStats(BuildCard(kCard1, 3, 4)); - remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - remote_.UpdateCardStats(BuildCard(kCard1, 3, 4)); + local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, false)); + local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); + remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, false)); + remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); MergeMetadata(&local_, &remote_); // These methods do not trigger notifications or sync: - local_.UpdateAddressStats(BuildAddress(kAddr1, 5, 6)); - local_.UpdateCardStats(BuildCard(kCard1, 7, 8)); - - EXPECT_CALL( - local_, - SendChangesToSyncServer(UnorderedElementsAre( - SyncChangeAndDataMatch( - syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag, - sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 5, 6), - SyncChangeAndDataMatch( - syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag, - sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 7, 8)))); + local_.UpdateAddressStats(BuildAddress(kAddr1, 5, 6, true)); + local_.UpdateCardStats(BuildCard(kCard1, 7, 8, kAddr2)); + + EXPECT_CALL(local_, SendChangesToSyncServer(UnorderedElementsAre( + SyncAddressChangeAndDataMatch( + syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag, + sync_pb::WalletMetadataSpecifics::ADDRESS, + kAddr1Utf8, 5, 6, true), + SyncCardChangeAndDataMatch( + syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag, + sync_pb::WalletMetadataSpecifics::CARD, + kCard1Utf8, 7, 8, kAddr2Utf8)))); local_.AutofillMultipleChanged(); } @@ -510,14 +569,14 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, // when multiple metadata values change at once. TEST_F(AutofillWalletMetadataSyncableServiceTest, DontUpdateToLowerValueOnServerOnMultiChange) { - local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - local_.UpdateCardStats(BuildCard(kCard1, 3, 4)); - remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - remote_.UpdateCardStats(BuildCard(kCard1, 3, 4)); + local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); + remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); MergeMetadata(&local_, &remote_); // These methods do not trigger notifications or sync: - local_.UpdateAddressStats(BuildAddress(kAddr1, 0, 0)); - local_.UpdateCardStats(BuildCard(kCard1, 0, 0)); + local_.UpdateAddressStats(BuildAddress(kAddr1, 0, 0, false)); + local_.UpdateCardStats(BuildCard(kCard1, 0, 0, kAddr2)); EXPECT_CALL(local_, SendChangesToSyncServer(_)).Times(0); @@ -528,10 +587,10 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, // multiple metadata values change at once. TEST_F(AutofillWalletMetadataSyncableServiceTest, DeleteFromServerOnMultiChange) { - local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - local_.UpdateCardStats(BuildCard(kCard1, 3, 4)); - remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - remote_.UpdateCardStats(BuildCard(kCard1, 3, 4)); + local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); + remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); MergeMetadata(&local_, &remote_); // This method dooes not trigger notifications or sync: local_.ClearServerData(); @@ -549,10 +608,10 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, // Verify that empty sync change from the sync server does not trigger writing // to disk or sending any data to the sync server. TEST_F(AutofillWalletMetadataSyncableServiceTest, EmptySyncChange) { - local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - local_.UpdateCardStats(BuildCard(kCard1, 3, 4)); - remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - remote_.UpdateCardStats(BuildCard(kCard1, 3, 4)); + local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); + remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); MergeMetadata(&local_, &remote_); EXPECT_CALL(local_, UpdateAddressStats(_)).Times(0); @@ -562,18 +621,49 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, EmptySyncChange) { local_.ProcessSyncChanges(FROM_HERE, syncer::SyncChangeList()); } -syncer::SyncChange BuildChange( +void BuildBasicChange(syncer::SyncChange::SyncChangeType change_type, + const std::string& sync_tag, + sync_pb::WalletMetadataSpecifics::Type metadata_type, + const std::string& server_id, + int64_t use_count, + int64_t use_date, + sync_pb::EntitySpecifics* entity) { + entity->mutable_wallet_metadata()->set_type(metadata_type); + entity->mutable_wallet_metadata()->set_id(server_id); + entity->mutable_wallet_metadata()->set_use_count(use_count); + entity->mutable_wallet_metadata()->set_use_date(use_date); +} + +syncer::SyncChange BuildAddressChange( + syncer::SyncChange::SyncChangeType change_type, + const std::string& sync_tag, + sync_pb::WalletMetadataSpecifics::Type metadata_type, + const std::string& server_id, + int64_t use_count, + int64_t use_date, + bool has_converted) { + sync_pb::EntitySpecifics entity; + BuildBasicChange(change_type, sync_tag, metadata_type, server_id, use_count, + use_date, &entity); + entity.mutable_wallet_metadata()->set_address_has_converted(has_converted); + return syncer::SyncChange( + FROM_HERE, change_type, + syncer::SyncData::CreateLocalData(sync_tag, sync_tag, entity)); +} + +syncer::SyncChange BuildCardChange( syncer::SyncChange::SyncChangeType change_type, const std::string& sync_tag, sync_pb::WalletMetadataSpecifics::Type metadata_type, const std::string& server_id, int64_t use_count, - int64_t use_date) { + int64_t use_date, + const std::string& billing_address_id) { sync_pb::EntitySpecifics entity; - entity.mutable_wallet_metadata()->set_type(metadata_type); - entity.mutable_wallet_metadata()->set_id(server_id); - entity.mutable_wallet_metadata()->set_use_count(use_count); - entity.mutable_wallet_metadata()->set_use_date(use_date); + BuildBasicChange(change_type, sync_tag, metadata_type, server_id, use_count, + use_date, &entity); + entity.mutable_wallet_metadata()->set_card_billing_address_id( + billing_address_id); return syncer::SyncChange( FROM_HERE, change_type, syncer::SyncData::CreateLocalData(sync_tag, sync_tag, entity)); @@ -584,18 +674,18 @@ syncer::SyncChange BuildChange( // server. TEST_F(AutofillWalletMetadataSyncableServiceTest, IgnoreNewMetadataFromServerOnSyncChange) { - local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - local_.UpdateCardStats(BuildCard(kCard1, 3, 4)); - remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - remote_.UpdateCardStats(BuildCard(kCard1, 3, 4)); + local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); + remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); MergeMetadata(&local_, &remote_); syncer::SyncChangeList changes; - changes.push_back(BuildChange(syncer::SyncChange::ACTION_ADD, kAddr2SyncTag, - sync_pb::WalletMetadataSpecifics::ADDRESS, - kAddr2Utf8, 5, 6)); - changes.push_back(BuildChange(syncer::SyncChange::ACTION_ADD, kCard2SyncTag, - sync_pb::WalletMetadataSpecifics::CARD, - kCard2Utf8, 7, 8)); + changes.push_back(BuildAddressChange( + syncer::SyncChange::ACTION_ADD, kAddr2SyncTag, + sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr2Utf8, 5, 6, true)); + changes.push_back(BuildCardChange( + syncer::SyncChange::ACTION_ADD, kCard2SyncTag, + sync_pb::WalletMetadataSpecifics::CARD, kCard2Utf8, 7, 8, kAddr2Utf8)); EXPECT_CALL(local_, UpdateAddressStats(_)).Times(0); EXPECT_CALL(local_, UpdateCardStats(_)).Times(0); @@ -608,22 +698,23 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, // disk when processing on-going sync changes. TEST_F(AutofillWalletMetadataSyncableServiceTest, SaveHigherValuesFromServerOnSyncChange) { - local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - local_.UpdateCardStats(BuildCard(kCard1, 3, 4)); - remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - remote_.UpdateCardStats(BuildCard(kCard1, 3, 4)); + local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, false)); + local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); + remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, false)); + remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); MergeMetadata(&local_, &remote_); syncer::SyncChangeList changes; - changes.push_back(BuildChange( + changes.push_back(BuildAddressChange( syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag, - sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 10, 20)); - changes.push_back( - BuildChange(syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag, - sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 30, 40)); - - EXPECT_CALL(local_, - UpdateAddressStats(AutofillMetadataMatches(kAddr1, 10, 20))); - EXPECT_CALL(local_, UpdateCardStats(AutofillMetadataMatches(kCard1, 30, 40))); + sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 10, 20, true)); + changes.push_back(BuildCardChange( + syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag, + sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 30, 40, kAddr2Utf8)); + + EXPECT_CALL(local_, UpdateAddressStats(AutofillAddressMetadataMatches( + kAddr1, 10, 20, true))); + EXPECT_CALL(local_, UpdateCardStats( + AutofillCardMetadataMatches(kCard1, 30, 40, kAddr2))); EXPECT_CALL(local_, SendChangesToSyncServer(_)).Times(0); local_.ProcessSyncChanges(FROM_HERE, changes); @@ -633,30 +724,30 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, // processing on-going sync changes. TEST_F(AutofillWalletMetadataSyncableServiceTest, SendHigherValuesToServerOnSyncChange) { - local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - local_.UpdateCardStats(BuildCard(kCard1, 3, 4)); - remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - remote_.UpdateCardStats(BuildCard(kCard1, 3, 4)); + local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); + remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); MergeMetadata(&local_, &remote_); syncer::SyncChangeList changes; - changes.push_back( - BuildChange(syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag, - sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 0, 0)); - changes.push_back( - BuildChange(syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag, - sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 0, 0)); + changes.push_back(BuildAddressChange( + syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag, + sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 0, 0, false)); + changes.push_back(BuildCardChange( + syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag, + sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 0, 0, kAddr2Utf8)); EXPECT_CALL(local_, UpdateAddressStats(_)).Times(0); EXPECT_CALL(local_, UpdateCardStats(_)).Times(0); - EXPECT_CALL( - local_, - SendChangesToSyncServer(UnorderedElementsAre( - SyncChangeAndDataMatch( - syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag, - sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 1, 2), - SyncChangeAndDataMatch( - syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag, - sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 3, 4)))); + EXPECT_CALL(local_, SendChangesToSyncServer(UnorderedElementsAre( + SyncAddressChangeAndDataMatch( + syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag, + sync_pb::WalletMetadataSpecifics::ADDRESS, + kAddr1Utf8, 1, 2, true), + SyncCardChangeAndDataMatch( + syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag, + sync_pb::WalletMetadataSpecifics::CARD, + kCard1Utf8, 3, 4, kAddr1Utf8)))); local_.ProcessSyncChanges(FROM_HERE, changes); } @@ -664,30 +755,30 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, // Verify that addition of known metadata is treated the same as an update. TEST_F(AutofillWalletMetadataSyncableServiceTest, TreatAdditionOfKnownMetadataAsUpdateOnSyncChange) { - local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - local_.UpdateCardStats(BuildCard(kCard1, 3, 4)); - remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - remote_.UpdateCardStats(BuildCard(kCard1, 3, 4)); + local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); + remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); MergeMetadata(&local_, &remote_); syncer::SyncChangeList changes; - changes.push_back(BuildChange(syncer::SyncChange::ACTION_ADD, kAddr1SyncTag, - sync_pb::WalletMetadataSpecifics::ADDRESS, - kAddr1Utf8, 0, 0)); - changes.push_back(BuildChange(syncer::SyncChange::ACTION_ADD, kCard1SyncTag, - sync_pb::WalletMetadataSpecifics::CARD, - kCard1Utf8, 0, 0)); + changes.push_back(BuildAddressChange( + syncer::SyncChange::ACTION_ADD, kAddr1SyncTag, + sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 0, 0, false)); + changes.push_back(BuildCardChange( + syncer::SyncChange::ACTION_ADD, kCard1SyncTag, + sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 0, 0, kAddr2Utf8)); EXPECT_CALL(local_, UpdateAddressStats(_)).Times(0); EXPECT_CALL(local_, UpdateCardStats(_)).Times(0); - EXPECT_CALL( - local_, - SendChangesToSyncServer(UnorderedElementsAre( - SyncChangeAndDataMatch( - syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag, - sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 1, 2), - SyncChangeAndDataMatch( - syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag, - sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 3, 4)))); + EXPECT_CALL(local_, SendChangesToSyncServer(UnorderedElementsAre( + SyncAddressChangeAndDataMatch( + syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag, + sync_pb::WalletMetadataSpecifics::ADDRESS, + kAddr1Utf8, 1, 2, true), + SyncCardChangeAndDataMatch( + syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag, + sync_pb::WalletMetadataSpecifics::CARD, + kCard1Utf8, 3, 4, kAddr1Utf8)))); local_.ProcessSyncChanges(FROM_HERE, changes); } @@ -696,18 +787,18 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, // no disk writes and no messages sent to the server. TEST_F(AutofillWalletMetadataSyncableServiceTest, IgnoreUpdateOfUnknownMetadataOnSyncChange) { - local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - local_.UpdateCardStats(BuildCard(kCard1, 3, 4)); - remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - remote_.UpdateCardStats(BuildCard(kCard1, 3, 4)); + local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); + remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); MergeMetadata(&local_, &remote_); syncer::SyncChangeList changes; - changes.push_back( - BuildChange(syncer::SyncChange::ACTION_UPDATE, kAddr2SyncTag, - sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr2Utf8, 0, 0)); - changes.push_back( - BuildChange(syncer::SyncChange::ACTION_UPDATE, kCard2SyncTag, - sync_pb::WalletMetadataSpecifics::CARD, kCard2Utf8, 0, 0)); + changes.push_back(BuildAddressChange( + syncer::SyncChange::ACTION_UPDATE, kAddr2SyncTag, + sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr2Utf8, 0, 0, false)); + changes.push_back(BuildCardChange( + syncer::SyncChange::ACTION_UPDATE, kCard2SyncTag, + sync_pb::WalletMetadataSpecifics::CARD, kCard2Utf8, 0, 0, kAddr2Utf8)); EXPECT_CALL(local_, UpdateAddressStats(_)).Times(0); EXPECT_CALL(local_, UpdateCardStats(_)).Times(0); @@ -720,18 +811,18 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, // ignored. There should be no disk writes and no messages sent to the server. TEST_F(AutofillWalletMetadataSyncableServiceTest, IgnoreDeleteOfUnknownMetadataOnSyncChange) { - local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - local_.UpdateCardStats(BuildCard(kCard1, 3, 4)); - remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - remote_.UpdateCardStats(BuildCard(kCard1, 3, 4)); + local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); + remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); MergeMetadata(&local_, &remote_); syncer::SyncChangeList changes; - changes.push_back( - BuildChange(syncer::SyncChange::ACTION_DELETE, kAddr2SyncTag, - sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr2Utf8, 0, 0)); - changes.push_back( - BuildChange(syncer::SyncChange::ACTION_DELETE, kCard2SyncTag, - sync_pb::WalletMetadataSpecifics::CARD, kCard2Utf8, 0, 0)); + changes.push_back(BuildAddressChange( + syncer::SyncChange::ACTION_DELETE, kAddr2SyncTag, + sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr2Utf8, 0, 0, false)); + changes.push_back(BuildCardChange( + syncer::SyncChange::ACTION_DELETE, kCard2SyncTag, + sync_pb::WalletMetadataSpecifics::CARD, kCard2Utf8, 0, 0, kAddr2Utf8)); EXPECT_CALL(local_, UpdateAddressStats(_)).Times(0); EXPECT_CALL(local_, UpdateCardStats(_)).Times(0); @@ -744,30 +835,30 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, // trigger an undelete message sent to the server. TEST_F(AutofillWalletMetadataSyncableServiceTest, UndeleteExistingMetadataOnSyncChange) { - local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - local_.UpdateCardStats(BuildCard(kCard1, 3, 4)); - remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - remote_.UpdateCardStats(BuildCard(kCard1, 3, 4)); + local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); + remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); MergeMetadata(&local_, &remote_); syncer::SyncChangeList changes; - changes.push_back( - BuildChange(syncer::SyncChange::ACTION_DELETE, kAddr1SyncTag, - sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 0, 0)); - changes.push_back( - BuildChange(syncer::SyncChange::ACTION_DELETE, kCard1SyncTag, - sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 0, 0)); + changes.push_back(BuildAddressChange( + syncer::SyncChange::ACTION_DELETE, kAddr1SyncTag, + sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 0, 0, false)); + changes.push_back(BuildCardChange( + syncer::SyncChange::ACTION_DELETE, kCard1SyncTag, + sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 0, 0, kAddr2Utf8)); EXPECT_CALL(local_, UpdateAddressStats(_)).Times(0); EXPECT_CALL(local_, UpdateCardStats(_)).Times(0); - EXPECT_CALL( - local_, - SendChangesToSyncServer(UnorderedElementsAre( - SyncChangeAndDataMatch(syncer::SyncChange::ACTION_ADD, kAddr1SyncTag, - sync_pb::WalletMetadataSpecifics::ADDRESS, - kAddr1Utf8, 1, 2), - SyncChangeAndDataMatch(syncer::SyncChange::ACTION_ADD, kCard1SyncTag, - sync_pb::WalletMetadataSpecifics::CARD, - kCard1Utf8, 3, 4)))); + EXPECT_CALL(local_, SendChangesToSyncServer(UnorderedElementsAre( + SyncAddressChangeAndDataMatch( + syncer::SyncChange::ACTION_ADD, kAddr1SyncTag, + sync_pb::WalletMetadataSpecifics::ADDRESS, + kAddr1Utf8, 1, 2, true), + SyncCardChangeAndDataMatch( + syncer::SyncChange::ACTION_ADD, kCard1SyncTag, + sync_pb::WalletMetadataSpecifics::CARD, + kCard1Utf8, 3, 4, kAddr1Utf8)))); local_.ProcessSyncChanges(FROM_HERE, changes); } @@ -776,22 +867,22 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, // data, which is used to avoid calling the expensive GetAllSyncData() function. TEST_F(AutofillWalletMetadataSyncableServiceTest, CacheIsUpToDateAfterSyncChange) { - local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - local_.UpdateAddressStats(BuildAddress(kAddr2, 3, 4)); - local_.UpdateCardStats(BuildCard(kCard1, 5, 6)); - local_.UpdateCardStats(BuildCard(kCard2, 7, 8)); - remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - remote_.UpdateAddressStats(BuildAddress(kAddr2, 3, 4)); - remote_.UpdateCardStats(BuildCard(kCard1, 5, 6)); - remote_.UpdateCardStats(BuildCard(kCard2, 7, 8)); + local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + local_.UpdateAddressStats(BuildAddress(kAddr2, 3, 4, false)); + local_.UpdateCardStats(BuildCard(kCard1, 5, 6, kAddr1)); + local_.UpdateCardStats(BuildCard(kCard2, 7, 8, kAddr2)); + remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + remote_.UpdateAddressStats(BuildAddress(kAddr2, 3, 4, false)); + remote_.UpdateCardStats(BuildCard(kCard1, 5, 6, kAddr1)); + remote_.UpdateCardStats(BuildCard(kCard2, 7, 8, kAddr2)); MergeMetadata(&local_, &remote_); syncer::SyncChangeList changes; - changes.push_back(BuildChange( + changes.push_back(BuildAddressChange( syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag, - sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 10, 20)); - changes.push_back( - BuildChange(syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag, - sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 50, 60)); + sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 10, 20, false)); + changes.push_back(BuildCardChange( + syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag, + sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 50, 60, kAddr1Utf8)); local_.ProcessSyncChanges(FROM_HERE, changes); // This method dooes not trigger notifications or sync: local_.ClearServerData(); @@ -812,23 +903,24 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, // values to the sync server. TEST_F(AutofillWalletMetadataSyncableServiceTest, SaveHigherValuesLocallyOnLateDataArrival) { - remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - remote_.UpdateCardStats(BuildCard(kCard1, 3, 4)); + remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, false)); + remote_.UpdateCardStats(BuildCard(kCard1, 5, 6, kAddr1)); MergeMetadata(&local_, &remote_); syncer::SyncChangeList changes; - changes.push_back( - BuildChange(syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag, - sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 5, 6)); - changes.push_back( - BuildChange(syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag, - sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 7, 8)); + changes.push_back(BuildAddressChange( + syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag, + sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 5, 6, true)); + changes.push_back(BuildCardChange( + syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag, + sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 7, 8, kAddr2Utf8)); local_.ProcessSyncChanges(FROM_HERE, changes); - local_.UpdateAddressStats(BuildAddress(kAddr1, 0, 0)); - local_.UpdateCardStats(BuildCard(kCard1, 0, 0)); + local_.UpdateAddressStats(BuildAddress(kAddr1, 0, 0, true)); + local_.UpdateCardStats(BuildCard(kCard1, 0, 0, kAddr2)); - EXPECT_CALL(local_, - UpdateAddressStats(AutofillMetadataMatches(kAddr1, 5, 6))); - EXPECT_CALL(local_, UpdateCardStats(AutofillMetadataMatches(kCard1, 7, 8))); + EXPECT_CALL(local_, UpdateAddressStats( + AutofillAddressMetadataMatches(kAddr1, 5, 6, true))); + EXPECT_CALL(local_, UpdateCardStats( + AutofillCardMetadataMatches(kCard1, 7, 8, kAddr2))); EXPECT_CALL(local_, SendChangesToSyncServer(_)).Times(0); local_.AutofillMultipleChanged(); @@ -839,42 +931,133 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, // once the data finally arrives. TEST_F(AutofillWalletMetadataSyncableServiceTest, SaveHigherValuesLocallyOnLateDataArrivalAfterPartialUpdates) { - remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2)); - remote_.UpdateAddressStats(BuildAddress(kAddr2, 3, 4)); - remote_.UpdateCardStats(BuildCard(kCard1, 5, 6)); - remote_.UpdateCardStats(BuildCard(kCard2, 7, 8)); + remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, false)); + remote_.UpdateAddressStats(BuildAddress(kAddr2, 3, 4, false)); + remote_.UpdateCardStats(BuildCard(kCard1, 5, 6, kAddr1)); + remote_.UpdateCardStats(BuildCard(kCard2, 7, 8, kAddr1)); MergeMetadata(&local_, &remote_); syncer::SyncChangeList changes; - changes.push_back(BuildChange( + changes.push_back(BuildAddressChange( syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag, - sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 9, 10)); - changes.push_back( - BuildChange(syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag, - sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 11, 12)); - changes.push_back(BuildChange( + sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr1Utf8, 9, 10, false)); + changes.push_back(BuildCardChange( + syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag, + sync_pb::WalletMetadataSpecifics::CARD, kCard1Utf8, 11, 12, kAddr2Utf8)); + changes.push_back(BuildAddressChange( syncer::SyncChange::ACTION_UPDATE, kAddr2SyncTag, - sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr2Utf8, 13, 14)); - changes.push_back( - BuildChange(syncer::SyncChange::ACTION_UPDATE, kCard2SyncTag, - sync_pb::WalletMetadataSpecifics::CARD, kCard2Utf8, 15, 16)); + sync_pb::WalletMetadataSpecifics::ADDRESS, kAddr2Utf8, 13, 14, true)); + changes.push_back(BuildCardChange( + syncer::SyncChange::ACTION_UPDATE, kCard2SyncTag, + sync_pb::WalletMetadataSpecifics::CARD, kCard2Utf8, 15, 16, kAddr1Utf8)); local_.ProcessSyncChanges(FROM_HERE, changes); changes.resize(2); local_.ProcessSyncChanges(FROM_HERE, changes); - local_.UpdateAddressStats(BuildAddress(kAddr1, 0, 0)); - local_.UpdateAddressStats(BuildAddress(kAddr2, 0, 0)); - local_.UpdateCardStats(BuildCard(kCard1, 0, 0)); - local_.UpdateCardStats(BuildCard(kCard2, 0, 0)); - - EXPECT_CALL(local_, - UpdateAddressStats(AutofillMetadataMatches(kAddr1, 9, 10))); - EXPECT_CALL(local_, UpdateCardStats(AutofillMetadataMatches(kCard1, 11, 12))); - EXPECT_CALL(local_, - UpdateAddressStats(AutofillMetadataMatches(kAddr2, 13, 14))); - EXPECT_CALL(local_, UpdateCardStats(AutofillMetadataMatches(kCard2, 15, 16))); + local_.UpdateAddressStats(BuildAddress(kAddr1, 0, 0, false)); + local_.UpdateAddressStats(BuildAddress(kAddr2, 0, 0, false)); + local_.UpdateCardStats(BuildCard(kCard1, 0, 0, kAddr1)); + local_.UpdateCardStats(BuildCard(kCard2, 0, 0, kAddr2)); + + EXPECT_CALL(local_, UpdateAddressStats(AutofillAddressMetadataMatches( + kAddr1, 9, 10, false))); + EXPECT_CALL(local_, UpdateCardStats( + AutofillCardMetadataMatches(kCard1, 11, 12, kAddr2))); + EXPECT_CALL(local_, UpdateAddressStats(AutofillAddressMetadataMatches( + kAddr2, 13, 14, true))); + EXPECT_CALL(local_, UpdateCardStats( + AutofillCardMetadataMatches(kCard2, 15, 16, kAddr1))); EXPECT_CALL(local_, SendChangesToSyncServer(_)).Times(0); local_.AutofillMultipleChanged(); } +// Verify that the merge logic keeps the best data on a field by field basis. +// Make sure that if the better data is split across the local and server +// version, both are updated with the merge results. +TEST_F(AutofillWalletMetadataSyncableServiceTest, SaveHigherValues_Mixed1) { + local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 20, true)); + local_.UpdateCardStats(BuildCard(kCard1, 30, 4, "")); + remote_.UpdateAddressStats(BuildAddress(kAddr1, 10, 2, false)); + remote_.UpdateCardStats(BuildCard(kCard1, 3, 40, kAddr1)); + + EXPECT_CALL(local_, UpdateAddressStats(AutofillAddressMetadataMatches( + kAddr1, 10, 20, true))); + EXPECT_CALL(local_, UpdateCardStats( + AutofillCardMetadataMatches(kCard1, 30, 40, kAddr1))); + EXPECT_CALL(local_, SendChangesToSyncServer(UnorderedElementsAre( + SyncAddressChangeAndDataMatch( + syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag, + sync_pb::WalletMetadataSpecifics::ADDRESS, + kAddr1Utf8, 10, 20, true), + SyncCardChangeAndDataMatch( + syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag, + sync_pb::WalletMetadataSpecifics::CARD, + kCard1Utf8, 30, 40, kAddr1Utf8)))); + + MergeMetadata(&local_, &remote_); +} + +// Verify that the merge logic keeps the best data on a field by field basis. +// Make sure that if the better data is split across the local and server +// version, both are updated with the merge results. +// Same as SaveHigherValues_Mixed1 but with the higher values moved from local +// to server and vice versa. +TEST_F(AutofillWalletMetadataSyncableServiceTest, SaveHigherValues_Mixed2) { + local_.UpdateAddressStats(BuildAddress(kAddr1, 10, 2, false)); + local_.UpdateCardStats(BuildCard(kCard1, 3, 40, kAddr1)); + remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 20, true)); + remote_.UpdateCardStats(BuildCard(kCard1, 30, 4, "")); + + EXPECT_CALL(local_, UpdateAddressStats(AutofillAddressMetadataMatches( + kAddr1, 10, 20, true))); + EXPECT_CALL(local_, UpdateCardStats( + AutofillCardMetadataMatches(kCard1, 30, 40, kAddr1))); + EXPECT_CALL(local_, SendChangesToSyncServer(UnorderedElementsAre( + SyncAddressChangeAndDataMatch( + syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag, + sync_pb::WalletMetadataSpecifics::ADDRESS, + kAddr1Utf8, 10, 20, true), + SyncCardChangeAndDataMatch( + syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag, + sync_pb::WalletMetadataSpecifics::CARD, + kCard1Utf8, 30, 40, kAddr1Utf8)))); + + MergeMetadata(&local_, &remote_); +} + +// Verify that if both local and server have a different non empty billing +// address id, the one with the most recent (bigger) use date is kept. +TEST_F(AutofillWalletMetadataSyncableServiceTest, + SaveHigherValues_DifferentBillingAddressId_LocalMostRecent) { + local_.UpdateCardStats(BuildCard(kCard1, 3, 40, kAddr1)); + remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr2)); + + // The value from the local should be kept because it has a more recent use + // date. + EXPECT_CALL(local_, UpdateCardStats(_)).Times(0); + EXPECT_CALL(local_, SendChangesToSyncServer( + UnorderedElementsAre(SyncCardChangeAndDataMatch( + syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag, + sync_pb::WalletMetadataSpecifics::CARD, + kCard1Utf8, 3, 40, kAddr1Utf8)))); + + MergeMetadata(&local_, &remote_); +} + +// Verify that if both local and server have a different non empty billing +// address id, the one with the most recent (bigger) use date is kept. +TEST_F(AutofillWalletMetadataSyncableServiceTest, + SaveHigherValues_DifferentBillingAddressId_RemoteMostRecent) { + local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); + remote_.UpdateCardStats(BuildCard(kCard1, 3, 40, kAddr2)); + + // The value from the remote should be kept because it has a more recent use + // date. + EXPECT_CALL(local_, UpdateCardStats( + AutofillCardMetadataMatches(kCard1, 3, 40, kAddr2))); + EXPECT_CALL(local_, SendChangesToSyncServer(_)).Times(0); + + MergeMetadata(&local_, &remote_); +} + } // namespace } // namespace autofill diff --git a/chromium/components/autofill/core/browser/webdata/autofill_wallet_syncable_service.cc b/chromium/components/autofill/core/browser/webdata/autofill_wallet_syncable_service.cc index de80ecd1728..8ad43337b41 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_wallet_syncable_service.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_wallet_syncable_service.cc @@ -12,8 +12,6 @@ #include "base/logging.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" -#include "components/autofill/core/browser/autofill_profile.h" -#include "components/autofill/core/browser/credit_card.h" #include "components/autofill/core/browser/webdata/autofill_table.h" #include "components/autofill/core/browser/webdata/autofill_webdata_backend.h" #include "components/autofill/core/browser/webdata/autofill_webdata_service.h" @@ -24,6 +22,10 @@ namespace autofill { namespace { +// The length of the GUIDs used for local autofill data. It is different than +// the length used for server autofill data. +const int kLocalGuidSize = 36; + void* UserDataKey() { // Use the address of a static so that COMDAT folding won't ever fold // with something else. @@ -75,6 +77,7 @@ CreditCard CardFromSpecifics(const sync_pb::WalletMaskedCreditCard& card) { base::UTF8ToUTF16(card.name_on_card())); result.SetExpirationMonth(card.exp_month()); result.SetExpirationYear(card.exp_year()); + result.set_billing_address_id(card.billing_address_id()); return result; } @@ -118,25 +121,6 @@ AutofillProfile ProfileFromSpecifics( return profile; } -// Searches for CreditCards with identical server IDs and copies the billing -// address ID from the existing cards on disk into the new cards from server. -// The credit card's IDs do not change over time. -void CopyBillingAddressesFromDisk(AutofillTable* table, - std::vector<CreditCard>* cards_from_server) { - std::vector<std::unique_ptr<CreditCard>> cards_on_disk; - table->GetServerCreditCards(&cards_on_disk); - - // The reasons behind brute-force search are explained in SetDataIfChanged. - for (const auto& saved_card : cards_on_disk) { - for (CreditCard& server_card : *cards_from_server) { - if (saved_card->server_id() == server_card.server_id()) { - server_card.set_billing_address_id(saved_card->billing_address_id()); - break; - } - } - } -} - // This function handles conditionally updating the AutofillTable with either // a set of CreditCards or AutocompleteProfiles only when the existing data // doesn't match. @@ -154,7 +138,7 @@ template <class Data> bool SetDataIfChanged( AutofillTable* table, const std::vector<Data>& data, - bool (AutofillTable::*getter)(std::vector<std::unique_ptr<Data>>*), + bool (AutofillTable::*getter)(std::vector<std::unique_ptr<Data>>*) const, void (AutofillTable::*setter)(const std::vector<Data>&), size_t* prev_item_count) { std::vector<std::unique_ptr<Data>> existing_data; @@ -270,10 +254,12 @@ void AutofillWalletSyncableService::InjectStartSyncFlare( flare_ = flare; } -syncer::SyncMergeResult AutofillWalletSyncableService::SetSyncData( - const syncer::SyncDataList& data_list) { - std::vector<CreditCard> wallet_cards; - std::vector<AutofillProfile> wallet_addresses; +// static +void AutofillWalletSyncableService::PopulateWalletCardsAndAddresses( + const syncer::SyncDataList& data_list, + std::vector<CreditCard>* wallet_cards, + std::vector<AutofillProfile>* wallet_addresses) { + std::map<std::string, std::string> ids; for (const syncer::SyncData& data : data_list) { DCHECK_EQ(syncer::AUTOFILL_WALLET_DATA, data.GetDataType()); @@ -281,23 +267,64 @@ syncer::SyncMergeResult AutofillWalletSyncableService::SetSyncData( data.GetSpecifics().autofill_wallet(); if (autofill_specifics.type() == sync_pb::AutofillWalletSpecifics::MASKED_CREDIT_CARD) { - wallet_cards.push_back( + wallet_cards->push_back( CardFromSpecifics(autofill_specifics.masked_card())); } else { DCHECK_EQ(sync_pb::AutofillWalletSpecifics::POSTAL_ADDRESS, autofill_specifics.type()); - wallet_addresses.push_back( + wallet_addresses->push_back( ProfileFromSpecifics(autofill_specifics.address())); + + // Map the sync billing address id to the profile's id. + ids[autofill_specifics.address().id()] = + wallet_addresses->back().server_id(); } } + // Set the billing address of the wallet cards to the id of the appropriate + // profile. + for (CreditCard& card : *wallet_cards) { + auto it = ids.find(card.billing_address_id()); + if (it != ids.end()) + card.set_billing_address_id(it->second); + } +} + +// static +void AutofillWalletSyncableService::CopyRelevantBillingAddressesFromDisk( + const AutofillTable& table, + std::vector<CreditCard>* cards_from_server) { + std::vector<std::unique_ptr<CreditCard>> cards_on_disk; + table.GetServerCreditCards(&cards_on_disk); + + // The reasons behind brute-force search are explained in SetDataIfChanged. + for (const auto& saved_card : cards_on_disk) { + for (CreditCard& server_card : *cards_from_server) { + if (saved_card->server_id() == server_card.server_id()) { + // Keep the billing address id of the saved cards only if it points to + // a local address. + if (saved_card->billing_address_id().length() == kLocalGuidSize) { + server_card.set_billing_address_id(saved_card->billing_address_id()); + break; + } + } + } + } +} + +syncer::SyncMergeResult AutofillWalletSyncableService::SetSyncData( + const syncer::SyncDataList& data_list) { + std::vector<CreditCard> wallet_cards; + std::vector<AutofillProfile> wallet_addresses; + PopulateWalletCardsAndAddresses(data_list, &wallet_cards, &wallet_addresses); + // Users can set billing address of the server credit card locally, but that // information does not propagate to either Chrome Sync or Google Payments // server. To preserve user's preferred billing address, copy the billing // addresses from disk into |wallet_cards|. AutofillTable* table = AutofillTable::FromWebDatabase(webdata_backend_->GetDatabase()); - CopyBillingAddressesFromDisk(table, &wallet_cards); + CopyRelevantBillingAddressesFromDisk(*table, &wallet_cards); // In the common case, the database won't have changed. Committing an update // to the database will require at least one DB page write and will schedule diff --git a/chromium/components/autofill/core/browser/webdata/autofill_wallet_syncable_service.h b/chromium/components/autofill/core/browser/webdata/autofill_wallet_syncable_service.h index eb4b9e044ef..03f13116409 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_wallet_syncable_service.h +++ b/chromium/components/autofill/core/browser/webdata/autofill_wallet_syncable_service.h @@ -8,10 +8,13 @@ #include "base/macros.h" #include "base/supports_user_data.h" #include "base/threading/thread_checker.h" +#include "components/autofill/core/browser/autofill_profile.h" +#include "components/autofill/core/browser/credit_card.h" #include "components/sync/model/syncable_service.h" namespace autofill { +class AutofillTable; class AutofillWebDataBackend; class AutofillWebDataService; @@ -58,8 +61,33 @@ class AutofillWalletSyncableService const std::string& app_locale); private: + FRIEND_TEST_ALL_PREFIXES( + AutofillWalletSyncableServiceTest, + CopyRelevantBillingAddressesFromDisk_KeepLocalAddresses); + FRIEND_TEST_ALL_PREFIXES( + AutofillWalletSyncableServiceTest, + CopyRelevantBillingAddressesFromDisk_OverwriteOtherAddresses); + FRIEND_TEST_ALL_PREFIXES( + AutofillWalletSyncableServiceTest, + PopulateWalletCardsAndAddresses_BillingAddressIdTransfer); + syncer::SyncMergeResult SetSyncData(const syncer::SyncDataList& data_list); + // Populates the wallet cards and addresses from the sync data and uses the + // sync data to link the card to its billing address. + static void PopulateWalletCardsAndAddresses( + const syncer::SyncDataList& data_list, + std::vector<CreditCard>* wallet_cards, + std::vector<AutofillProfile>* wallet_addresses); + + // Finds the copies of the same credit card from the server and on disk and + // overwrites the server version with the billing id saved on disk if it + // refers to a local autofill profile. The credit card's IDs do not change + // over time. + static void CopyRelevantBillingAddressesFromDisk( + const AutofillTable& table, + std::vector<CreditCard>* cards_from_server); + base::ThreadChecker thread_checker_; AutofillWebDataBackend* webdata_backend_; // Weak ref. diff --git a/chromium/components/autofill/core/browser/webdata/autofill_wallet_syncable_service_unittest.cc b/chromium/components/autofill/core/browser/webdata/autofill_wallet_syncable_service_unittest.cc new file mode 100644 index 00000000000..9561a8b1c9b --- /dev/null +++ b/chromium/components/autofill/core/browser/webdata/autofill_wallet_syncable_service_unittest.cc @@ -0,0 +1,165 @@ +// 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/core/browser/webdata/autofill_wallet_syncable_service.h" + +#include <vector> + +#include "base/memory/ptr_util.h" +#include "components/autofill/core/browser/autofill_profile.h" +#include "components/autofill/core/browser/credit_card.h" +#include "components/autofill/core/browser/webdata/autofill_table.h" +#include "components/sync/protocol/autofill_specifics.pb.h" +#include "components/sync/protocol/sync.pb.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace autofill { +namespace { + +syncer::SyncData CreateSyncDataForWalletCreditCard( + const std::string& id, + const std::string& billing_address_id) { + sync_pb::EntitySpecifics specifics; + + sync_pb::AutofillWalletSpecifics* wallet_specifics = + specifics.mutable_autofill_wallet(); + wallet_specifics->set_type( + sync_pb::AutofillWalletSpecifics_WalletInfoType:: + AutofillWalletSpecifics_WalletInfoType_MASKED_CREDIT_CARD); + + sync_pb::WalletMaskedCreditCard* card_specifics = + wallet_specifics->mutable_masked_card(); + card_specifics->set_id(id); + card_specifics->set_billing_address_id(billing_address_id); + return syncer::SyncData::CreateLocalData(id, id, specifics); +} + +syncer::SyncData CreateSyncDataForWalletAddress(const std::string& id) { + sync_pb::EntitySpecifics specifics; + + sync_pb::AutofillWalletSpecifics* wallet_specifics = + specifics.mutable_autofill_wallet(); + wallet_specifics->set_type( + sync_pb::AutofillWalletSpecifics_WalletInfoType:: + AutofillWalletSpecifics_WalletInfoType_POSTAL_ADDRESS); + + sync_pb::WalletPostalAddress* address_specifics = + wallet_specifics->mutable_address(); + address_specifics->set_id(id); + return syncer::SyncData::CreateLocalData(id, id, specifics); +} + +class TestAutofillTable : public AutofillTable { + public: + explicit TestAutofillTable(std::vector<CreditCard> cards_on_disk) + : cards_on_disk_(cards_on_disk) {} + + ~TestAutofillTable() override {} + + bool GetServerCreditCards( + std::vector<std::unique_ptr<CreditCard>>* cards) const override { + for (const auto& card_on_disk : cards_on_disk_) + cards->push_back(base::MakeUnique<CreditCard>(card_on_disk)); + return true; + } + + private: + std::vector<CreditCard> cards_on_disk_; + + DISALLOW_COPY_AND_ASSIGN(TestAutofillTable); +}; + +} // anonymous namespace + +// Verify that the link between a card and its billing address from sync is +// present in the generated Autofill objects. +TEST(AutofillWalletSyncableServiceTest, + PopulateWalletCardsAndAddresses_BillingAddressIdTransfer) { + std::vector<CreditCard> wallet_cards; + std::vector<AutofillProfile> wallet_addresses; + syncer::SyncDataList data_list; + + // Create a Sync data for a card and its billing address. + data_list.push_back(CreateSyncDataForWalletAddress("1" /* id */)); + data_list.push_back(CreateSyncDataForWalletCreditCard( + "card1" /* id */, "1" /* billing_address_id */)); + + AutofillWalletSyncableService::PopulateWalletCardsAndAddresses( + data_list, &wallet_cards, &wallet_addresses); + + ASSERT_EQ(1U, wallet_cards.size()); + ASSERT_EQ(1U, wallet_addresses.size()); + + // Make sure the card's billing address id is equal to the address' server id. + EXPECT_EQ(wallet_addresses.back().server_id(), + wallet_cards.back().billing_address_id()); +} + +// Verify that the billing address id from the card saved on disk is kept if it +// is a local profile guid. +TEST(AutofillWalletSyncableServiceTest, + CopyRelevantBillingAddressesFromDisk_KeepLocalAddresses) { + std::vector<CreditCard> cards_on_disk; + std::vector<CreditCard> wallet_cards; + + // Create a local profile to be used as a billing address. + AutofillProfile billing_address; + + // Create a card on disk that refers to that local profile as its billing + // address. + cards_on_disk.push_back(CreditCard()); + cards_on_disk.back().set_billing_address_id(billing_address.guid()); + + // Create a card pulled from wallet with the same id, but a different billing + // address id. + wallet_cards.push_back(CreditCard(cards_on_disk.back())); + wallet_cards.back().set_billing_address_id("1234"); + + // Setup the TestAutofillTable with the cards_on_disk. + TestAutofillTable table(cards_on_disk); + + AutofillWalletSyncableService::CopyRelevantBillingAddressesFromDisk( + table, &wallet_cards); + + ASSERT_EQ(1U, wallet_cards.size()); + + // Make sure the wallet card replace its billind address id for the one that + // was saved on disk. + EXPECT_EQ(cards_on_disk.back().billing_address_id(), + wallet_cards.back().billing_address_id()); +} + +// Verify that the billing address id from the card saved on disk is overwritten +// if it does not refer to a local profile. +TEST(AutofillWalletSyncableServiceTest, + CopyRelevantBillingAddressesFromDisk_OverwriteOtherAddresses) { + std::string old_billing_id = "1234"; + std::string new_billing_id = "9876"; + std::vector<CreditCard> cards_on_disk; + std::vector<CreditCard> wallet_cards; + + // Create a card on disk that does not refer to a local profile (which have 36 + // chars ids). + cards_on_disk.push_back(CreditCard()); + cards_on_disk.back().set_billing_address_id(old_billing_id); + + // Create a card pulled from wallet with the same id, but a different billing + // address id. + wallet_cards.push_back(CreditCard(cards_on_disk.back())); + wallet_cards.back().set_billing_address_id(new_billing_id); + + // Setup the TestAutofillTable with the cards_on_disk. + TestAutofillTable table(cards_on_disk); + + AutofillWalletSyncableService::CopyRelevantBillingAddressesFromDisk( + table, &wallet_cards); + + ASSERT_EQ(1U, wallet_cards.size()); + + // Make sure the local address billing id that was saved on disk did not + // replace the new one. + EXPECT_EQ(new_billing_id, wallet_cards.back().billing_address_id()); +} + +} // namespace autofill
\ No newline at end of file diff --git a/chromium/components/autofill/core/browser/webdata/autofill_webdata.h b/chromium/components/autofill/core/browser/webdata/autofill_webdata.h index 3cdd07322a9..eb573874b12 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_webdata.h +++ b/chromium/components/autofill/core/browser/webdata/autofill_webdata.h @@ -17,7 +17,6 @@ class Time; } // namespace base -class Profile; class WebDataServiceConsumer; namespace autofill { @@ -110,16 +109,11 @@ class AutofillWebData { const base::string16& full_number) = 0; virtual void MaskServerCreditCard(const std::string& id) = 0; - // Updates the use count and last use date for a server card (masked or not). - virtual void UpdateServerCardUsageStats(const CreditCard& credit_card) = 0; + // Updates the metadata for a server card (masked or not). + virtual void UpdateServerCardMetadata(const CreditCard& credit_card) = 0; - // Updates the use count and last use date for a server address. - virtual void UpdateServerAddressUsageStats(const AutofillProfile& profile) - = 0; - - // Updates the billing address for a server card (masked or not). - virtual void UpdateServerCardBillingAddress(const CreditCard& credit_card) - = 0; + // Updates the metadata for a server address. + virtual void UpdateServerAddressMetadata(const AutofillProfile& profile) = 0; // Removes Autofill records from the database. virtual void RemoveAutofillDataModifiedBetween( 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 ccda7ac2eca..2129da0abdd 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 @@ -29,14 +29,12 @@ AutofillWebDataBackendImpl::AutofillWebDataBackendImpl( scoped_refptr<base::SingleThreadTaskRunner> db_thread, const base::Closure& on_changed_callback, const base::Callback<void(syncer::ModelType)>& on_sync_started_callback) - : base::RefCountedDeleteOnMessageLoop<AutofillWebDataBackendImpl>( - db_thread), + : base::RefCountedDeleteOnSequence<AutofillWebDataBackendImpl>(db_thread), ui_thread_(ui_thread), db_thread_(db_thread), web_database_backend_(web_database_backend), on_changed_callback_(on_changed_callback), - on_sync_started_callback_(on_sync_started_callback) { -} + on_sync_started_callback_(on_sync_started_callback) {} void AutofillWebDataBackendImpl::AddObserver( AutofillWebDataServiceObserverOnDBThread* observer) { @@ -372,11 +370,11 @@ WebDatabase::State return WebDatabase::COMMIT_NOT_NEEDED; } -WebDatabase::State AutofillWebDataBackendImpl::UpdateServerCardUsageStats( +WebDatabase::State AutofillWebDataBackendImpl::UpdateServerCardMetadata( const CreditCard& card, WebDatabase* db) { DCHECK(db_thread_->BelongsToCurrentThread()); - if (!AutofillTable::FromWebDatabase(db)->UpdateServerCardUsageStats(card)) + if (!AutofillTable::FromWebDatabase(db)->UpdateServerCardMetadata(card)) return WebDatabase::COMMIT_NOT_NEEDED; for (auto& db_observer : db_observer_list_) { @@ -387,11 +385,11 @@ WebDatabase::State AutofillWebDataBackendImpl::UpdateServerCardUsageStats( return WebDatabase::COMMIT_NEEDED; } -WebDatabase::State AutofillWebDataBackendImpl::UpdateServerAddressUsageStats( +WebDatabase::State AutofillWebDataBackendImpl::UpdateServerAddressMetadata( const AutofillProfile& profile, WebDatabase* db) { DCHECK(db_thread_->BelongsToCurrentThread()); - if (!AutofillTable::FromWebDatabase(db)->UpdateServerAddressUsageStats( + if (!AutofillTable::FromWebDatabase(db)->UpdateServerAddressMetadata( profile)) { return WebDatabase::COMMIT_NOT_NEEDED; } @@ -404,23 +402,6 @@ WebDatabase::State AutofillWebDataBackendImpl::UpdateServerAddressUsageStats( return WebDatabase::COMMIT_NEEDED; } -WebDatabase::State AutofillWebDataBackendImpl::UpdateServerCardBillingAddress( - const CreditCard& card, - WebDatabase* db) { - DCHECK(db_thread_->BelongsToCurrentThread()); - if (!AutofillTable::FromWebDatabase(db)->UpdateServerCardBillingAddress( - card)) { - return WebDatabase::COMMIT_NOT_NEEDED; - } - - for (auto& db_observer : db_observer_list_) { - db_observer.CreditCardChanged( - CreditCardChange(CreditCardChange::UPDATE, card.guid(), &card)); - } - - return WebDatabase::COMMIT_NEEDED; -} - WebDatabase::State AutofillWebDataBackendImpl::ClearAllServerData( WebDatabase* db) { DCHECK(db_thread_->BelongsToCurrentThread()); 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 516e4d14151..9badefeb70c 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 @@ -9,7 +9,7 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" -#include "base/memory/ref_counted_delete_on_message_loop.h" +#include "base/memory/ref_counted_delete_on_sequence.h" #include "base/observer_list.h" #include "base/supports_user_data.h" #include "components/autofill/core/browser/webdata/autofill_webdata.h" @@ -39,7 +39,7 @@ class CreditCard; // WebDataService. // This class is destroyed on the DB thread. class AutofillWebDataBackendImpl - : public base::RefCountedDeleteOnMessageLoop<AutofillWebDataBackendImpl>, + : public base::RefCountedDeleteOnSequence<AutofillWebDataBackendImpl>, public AutofillWebDataBackend { public: // |web_database_backend| is used to access the WebDatabase directly for @@ -152,17 +152,11 @@ class AutofillWebDataBackendImpl WebDatabase::State MaskServerCreditCard(const std::string& id, WebDatabase* db); - WebDatabase::State UpdateServerCardUsageStats( - const CreditCard& credit_card, - WebDatabase* db); - - WebDatabase::State UpdateServerAddressUsageStats( - const AutofillProfile& profile, - WebDatabase* db); + WebDatabase::State UpdateServerCardMetadata(const CreditCard& credit_card, + WebDatabase* db); - WebDatabase::State UpdateServerCardBillingAddress( - const CreditCard& card, - WebDatabase* db); + WebDatabase::State UpdateServerAddressMetadata(const AutofillProfile& profile, + WebDatabase* db); WebDatabase::State ClearAllServerData(WebDatabase* db); @@ -184,7 +178,7 @@ class AutofillWebDataBackendImpl ~AutofillWebDataBackendImpl() override; private: - friend class base::RefCountedDeleteOnMessageLoop<AutofillWebDataBackendImpl>; + friend class base::RefCountedDeleteOnSequence<AutofillWebDataBackendImpl>; friend class base::DeleteHelper<AutofillWebDataBackendImpl>; // This makes the destructor public, and thus allows us to aggregate 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 22377e9e3af..d8d072f0d43 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.cc @@ -212,28 +212,18 @@ void AutofillWebDataService::ClearAllServerData() { autofill_backend_)); } -void AutofillWebDataService::UpdateServerCardUsageStats( +void AutofillWebDataService::UpdateServerCardMetadata( const CreditCard& credit_card) { wdbs_->ScheduleDBTask( - FROM_HERE, - Bind(&AutofillWebDataBackendImpl::UpdateServerCardUsageStats, - autofill_backend_, credit_card)); + FROM_HERE, Bind(&AutofillWebDataBackendImpl::UpdateServerCardMetadata, + autofill_backend_, credit_card)); } -void AutofillWebDataService::UpdateServerAddressUsageStats( +void AutofillWebDataService::UpdateServerAddressMetadata( const AutofillProfile& profile) { wdbs_->ScheduleDBTask( - FROM_HERE, - Bind(&AutofillWebDataBackendImpl::UpdateServerAddressUsageStats, - autofill_backend_, profile)); -} - -void AutofillWebDataService::UpdateServerCardBillingAddress( - const CreditCard& credit_card) { - wdbs_->ScheduleDBTask( - FROM_HERE, - Bind(&AutofillWebDataBackendImpl::UpdateServerCardBillingAddress, - autofill_backend_, credit_card)); + FROM_HERE, Bind(&AutofillWebDataBackendImpl::UpdateServerAddressMetadata, + autofill_backend_, profile)); } void AutofillWebDataService::RemoveAutofillDataModifiedBetween( 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 ab0ac2c86e5..dfc35afca5f 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.h +++ b/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.h @@ -28,7 +28,6 @@ class SingleThreadTaskRunner; namespace autofill { -class AutofillChange; class AutofillEntry; class AutofillProfile; class AutofillWebDataBackend; @@ -98,9 +97,8 @@ class AutofillWebDataService : public AutofillWebData, void ClearAllServerData(); - void UpdateServerCardUsageStats(const CreditCard& credit_card) override; - void UpdateServerAddressUsageStats(const AutofillProfile& profile) override; - void UpdateServerCardBillingAddress(const CreditCard& credit_card) override; + void UpdateServerCardMetadata(const CreditCard& credit_card) override; + void UpdateServerAddressMetadata(const AutofillProfile& profile) override; void RemoveAutofillDataModifiedBetween(const base::Time& delete_begin, const base::Time& delete_end) override; diff --git a/chromium/components/autofill/core/common/autofill_regexes.cc b/chromium/components/autofill/core/common/autofill_regexes.cc index dc3dd58e04b..c9fa5222ac1 100644 --- a/chromium/components/autofill/core/common/autofill_regexes.cc +++ b/chromium/components/autofill/core/common/autofill_regexes.cc @@ -5,9 +5,9 @@ #include "components/autofill/core/common/autofill_regexes.h" #include <memory> +#include <unordered_map> #include <utility> -#include "base/containers/scoped_ptr_hash_map.h" #include "base/logging.h" #include "base/macros.h" #include "base/memory/singleton.h" @@ -30,7 +30,7 @@ class AutofillRegexes { friend struct base::DefaultSingletonTraits<AutofillRegexes>; // Maps patterns to their corresponding regex matchers. - base::ScopedPtrHashMap<base::string16, std::unique_ptr<icu::RegexMatcher>> + std::unordered_map<base::string16, std::unique_ptr<icu::RegexMatcher>> matchers_; DISALLOW_COPY_AND_ASSIGN(AutofillRegexes); @@ -57,11 +57,11 @@ icu::RegexMatcher* AutofillRegexes::GetMatcher(const base::string16& pattern) { new icu::RegexMatcher(icu_pattern, UREGEX_CASE_INSENSITIVE, status)); DCHECK(U_SUCCESS(status)); - auto result = matchers_.add(pattern, std::move(matcher)); + auto result = matchers_.insert(std::make_pair(pattern, std::move(matcher))); DCHECK(result.second); it = result.first; } - return it->second; + return it->second.get(); } } // namespace diff --git a/chromium/components/autofill/core/common/password_form.cc b/chromium/components/autofill/core/common/password_form.cc index 17db56672b2..3969e1ff4e4 100644 --- a/chromium/components/autofill/core/common/password_form.cc +++ b/chromium/components/autofill/core/common/password_form.cc @@ -145,8 +145,9 @@ bool ArePasswordFormUniqueKeyEqual(const PasswordForm& left, left.password_element == right.password_element); } -bool LessThanUniqueKey::operator()(const PasswordForm* left, - const PasswordForm* right) const { +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; diff --git a/chromium/components/autofill/core/common/password_form.h b/chromium/components/autofill/core/common/password_form.h index c70da901fc9..74716230236 100644 --- a/chromium/components/autofill/core/common/password_form.h +++ b/chromium/components/autofill/core/common/password_form.h @@ -165,6 +165,10 @@ struct PasswordForm { // element corresponding to the new password. Optional, and not persisted. base::string16 new_password_element; + // The confirmation password element. Optional, only set on form parsing, and + // not persisted. + base::string16 confirmation_password_element; + // The new password. Optional, and not persisted. base::string16 new_password_value; @@ -293,7 +297,8 @@ bool ArePasswordFormUniqueKeyEqual(const PasswordForm& left, // A comparator for the unique key. struct LessThanUniqueKey { - bool operator()(const PasswordForm* left, const PasswordForm* right) const; + bool operator()(const std::unique_ptr<PasswordForm>& left, + const std::unique_ptr<PasswordForm>& right) const; }; // For testing. 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 b4d5fd047bc..9b1383046e0 100644 --- a/chromium/components/autofill/core/common/password_form_fill_data.cc +++ b/chromium/components/autofill/core/common/password_form_fill_data.cc @@ -22,9 +22,7 @@ bool UsernamesCollectionKey::operator<( } PasswordFormFillData::PasswordFormFillData() - : wait_for_username(false), - is_possible_change_password_form(false) { -} + : wait_for_username(false), is_possible_change_password_form(false) {} PasswordFormFillData::PasswordFormFillData(const PasswordFormFillData& other) = default; 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 b2dd78b63c8..93d1bc6c500 100644 --- a/chromium/components/autofill/core/common/save_password_progress_logger.cc +++ b/chromium/components/autofill/core/common/save_password_progress_logger.cc @@ -266,6 +266,8 @@ std::string SavePasswordProgressLogger::GetStringFromID( return "Invalid form"; case SavePasswordProgressLogger::STRING_SYNC_CREDENTIAL: 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_IGNORE_POSSIBLE_USERNAMES: @@ -318,11 +320,11 @@ std::string SavePasswordProgressLogger::GetStringFromID( case SavePasswordProgressLogger::STRING_BEST_SCORE: return "best_score"; case SavePasswordProgressLogger::STRING_ON_GET_STORE_RESULTS_METHOD: - return "PasswordFormManager::OnGetPasswordStoreResults"; + return "FormFetcherImpl::OnGetPasswordStoreResults"; case SavePasswordProgressLogger::STRING_NUMBER_RESULTS: return "Number of results from the password store"; - case SavePasswordProgressLogger::STRING_FETCH_LOGINS_METHOD: - return "PasswordFormManager::FetchMatchingLoginsFromPasswordStore"; + case SavePasswordProgressLogger::STRING_FETCH_METHOD: + return "FormFetcherImpl::Fetch"; case SavePasswordProgressLogger::STRING_NO_STORE: return "PasswordStore is not available"; case SavePasswordProgressLogger::STRING_CREATE_LOGIN_MANAGERS_METHOD: @@ -346,8 +348,8 @@ std::string SavePasswordProgressLogger::GetStringFromID( return "PasswordFormManager::ProcessFrame"; case SavePasswordProgressLogger::STRING_FORM_SIGNATURE: return "Signature of form"; - case SavePasswordProgressLogger::STRING_FORM_MANAGER_STATE: - return "PasswordFormManager::state_"; + case SavePasswordProgressLogger::STRING_FORM_FETCHER_STATE: + return "FormFetcherImpl::state_"; case SavePasswordProgressLogger::STRING_ADDING_SIGNATURE: return "Adding manager for form"; case SavePasswordProgressLogger::STRING_UNOWNED_INPUTS_VISIBLE: @@ -388,6 +390,8 @@ std::string SavePasswordProgressLogger::GetStringFromID( return "Server predictions"; case SavePasswordProgressLogger::STRING_FORM_VOTES: return "Form votes"; + case SavePasswordProgressLogger::STRING_REUSE_FOUND: + return "Password reused from "; 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 d8c9fe1ac08..de8a08232b4 100644 --- a/chromium/components/autofill/core/common/save_password_progress_logger.h +++ b/chromium/components/autofill/core/common/save_password_progress_logger.h @@ -90,6 +90,7 @@ class SavePasswordProgressLogger { STRING_FORM_BLACKLISTED, STRING_INVALID_FORM, STRING_SYNC_CREDENTIAL, + STRING_BLOCK_PASSWORD_SAME_ORIGIN_INSECURE_SCHEME, STRING_PROVISIONALLY_SAVED_FORM, STRING_IGNORE_POSSIBLE_USERNAMES, STRING_ON_PASSWORD_FORMS_RENDERED_METHOD, @@ -116,7 +117,7 @@ class SavePasswordProgressLogger { STRING_BEST_SCORE, STRING_ON_GET_STORE_RESULTS_METHOD, STRING_NUMBER_RESULTS, - STRING_FETCH_LOGINS_METHOD, + STRING_FETCH_METHOD, STRING_NO_STORE, STRING_CREATE_LOGIN_MANAGERS_METHOD, STRING_OLD_NUMBER_LOGIN_MANAGERS, @@ -129,7 +130,7 @@ class SavePasswordProgressLogger { STRING_PROCESS_FRAME_METHOD, STRING_FORM_SIGNATURE, STRING_ADDING_SIGNATURE, - STRING_FORM_MANAGER_STATE, + STRING_FORM_FETCHER_STATE, STRING_UNOWNED_INPUTS_VISIBLE, STRING_ON_FILL_PASSWORD_FORM_METHOD, STRING_ON_SHOW_INITIAL_PASSWORD_ACCOUNT_SUGGESTIONS, @@ -148,6 +149,7 @@ class SavePasswordProgressLogger { STRING_FIELDS, STRING_SERVER_PREDICTIONS, STRING_FORM_VOTES, + STRING_REUSE_FOUND, STRING_INVALID, // Represents a string returned in a case of an error. STRING_MAX = STRING_INVALID }; diff --git a/chromium/components/autofill/ios/browser/BUILD.gn b/chromium/components/autofill/ios/browser/BUILD.gn index 91dbed34da0..c16d7d83090 100644 --- a/chromium/components/autofill/ios/browser/BUILD.gn +++ b/chromium/components/autofill/ios/browser/BUILD.gn @@ -27,7 +27,6 @@ source_set("browser") { "//base", "//components/autofill/core/browser", "//components/autofill/core/common", - "//ios/public/provider/web", "//ios/web", "//ui/gfx/geometry", ] diff --git a/chromium/components/autofill/ios/browser/autofill_driver_ios.h b/chromium/components/autofill/ios/browser/autofill_driver_ios.h index 2da12651251..c6678f31c49 100644 --- a/chromium/components/autofill/ios/browser/autofill_driver_ios.h +++ b/chromium/components/autofill/ios/browser/autofill_driver_ios.h @@ -21,8 +21,6 @@ class WebState; namespace autofill { -class AutofillManagerDelegate; - // Class that drives autofill flow on iOS. There is one instance per // WebContents. class AutofillDriverIOS : public AutofillDriver, diff --git a/chromium/components/autofill/ios/browser/form_suggestion.h b/chromium/components/autofill/ios/browser/form_suggestion.h index 7fd9df1ddbd..8fb6689d694 100644 --- a/chromium/components/autofill/ios/browser/form_suggestion.h +++ b/chromium/components/autofill/ios/browser/form_suggestion.h @@ -7,8 +7,6 @@ #import <Foundation/Foundation.h> -#include "base/mac/objc_property_releaser.h" - // Represents a user-selectable suggestion for a single field within a form // on a web page. @interface FormSuggestion : NSObject diff --git a/chromium/components/autofill/ios/browser/form_suggestion.mm b/chromium/components/autofill/ios/browser/form_suggestion.mm index 709bb63b4ce..f4bb40c37c3 100644 --- a/chromium/components/autofill/ios/browser/form_suggestion.mm +++ b/chromium/components/autofill/ios/browser/form_suggestion.mm @@ -4,6 +4,8 @@ #import "components/autofill/ios/browser/form_suggestion.h" +#include "base/mac/objc_property_releaser.h" + @interface FormSuggestion () // Local initializer for a FormSuggestion. - (id)initWithValue:(NSString*)value |