diff options
Diffstat (limited to 'chromium/components/autofill')
111 files changed, 4784 insertions, 1363 deletions
diff --git a/chromium/components/autofill/DEPS b/chromium/components/autofill/DEPS index 6b6ca5cf0be..15a0bfa7044 100644 --- a/chromium/components/autofill/DEPS +++ b/chromium/components/autofill/DEPS @@ -1,7 +1,7 @@ include_rules = [ "+components/prefs", + "+components/strings/grit/components_strings.h", "+google_apis/gaia/gaia_urls.h", - "+grit", # For generated headers "+jni", "+net", "+third_party/skia", diff --git a/chromium/components/autofill/OWNERS b/chromium/components/autofill/OWNERS index d52021494e9..585db25ef2c 100644 --- a/chromium/components/autofill/OWNERS +++ b/chromium/components/autofill/OWNERS @@ -1,5 +1,6 @@ estade@chromium.org mathp@chromium.org +rogerm@chromium.org sebsg@chromium.org vabr@chromium.org @@ -7,3 +8,5 @@ vabr@chromium.org # TODO(gcasto): Change to per-file ownership after http://crbug.com/235756 # is fixed. gcasto@chromium.org + +# COMPONENT: UI>Browser>Autofill diff --git a/chromium/components/autofill/content/browser/content_autofill_driver.cc b/chromium/components/autofill/content/browser/content_autofill_driver.cc index 8eb8de071ae..710a1e74c9a 100644 --- a/chromium/components/autofill/content/browser/content_autofill_driver.cc +++ b/chromium/components/autofill/content/browser/content_autofill_driver.cc @@ -17,7 +17,7 @@ #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/navigation_controller.h" -#include "content/public/browser/navigation_details.h" +#include "content/public/browser/navigation_handle.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/render_widget_host_view.h" @@ -240,9 +240,8 @@ void ContentAutofillDriver::SetDataList( } void ContentAutofillDriver::DidNavigateFrame( - const content::LoadCommittedDetails& details, - const content::FrameNavigateParams& params) { - if (details.is_navigation_to_different_page()) + content::NavigationHandle* navigation_handle) { + if (navigation_handle->IsInMainFrame() && !navigation_handle->IsSamePage()) autofill_manager_->Reset(); } diff --git a/chromium/components/autofill/content/browser/content_autofill_driver.h b/chromium/components/autofill/content/browser/content_autofill_driver.h index 2fd226efc69..0f6fb084027 100644 --- a/chromium/components/autofill/content/browser/content_autofill_driver.h +++ b/chromium/components/autofill/content/browser/content_autofill_driver.h @@ -17,9 +17,8 @@ #include "mojo/public/cpp/bindings/binding.h" namespace content { +class NavigationHandle; class RenderFrameHost; -struct FrameNavigateParams; -struct LoadCommittedDetails; } namespace autofill { @@ -68,6 +67,7 @@ class ContentAutofillDriver : public AutofillDriver, gfx::RectF TransformBoundingBoxToViewportCoordinates( const gfx::RectF& bounding_box) override; void DidInteractWithCreditCardForm() override; + void NotifyFirstUserGestureObservedInTab() override; // mojom::AutofillDriver: void FirstUserGestureObserved() override; @@ -92,12 +92,7 @@ class ContentAutofillDriver : public AutofillDriver, const std::vector<base::string16>& labels) override; // Called when the frame has navigated. - void DidNavigateFrame(const content::LoadCommittedDetails& details, - const content::FrameNavigateParams& params); - - // Tells the render frame that a user gesture was observed - // somewhere in the tab (including in a different frame). - void NotifyFirstUserGestureObservedInTab(); + void DidNavigateFrame(content::NavigationHandle* navigation_handle); AutofillExternalDelegate* autofill_external_delegate() { return &autofill_external_delegate_; diff --git a/chromium/components/autofill/content/browser/content_autofill_driver_factory.cc b/chromium/components/autofill/content/browser/content_autofill_driver_factory.cc index 32465df8fa4..3865c037ec6 100644 --- a/chromium/components/autofill/content/browser/content_autofill_driver_factory.cc +++ b/chromium/components/autofill/content/browser/content_autofill_driver_factory.cc @@ -4,21 +4,32 @@ #include "components/autofill/content/browser/content_autofill_driver_factory.h" +#include <utility> #include <vector> +#include "base/bind.h" #include "base/memory/ptr_util.h" -#include "base/stl_util.h" #include "components/autofill/content/browser/content_autofill_driver.h" -#include "components/autofill/core/browser/autofill_client.h" #include "components/autofill/core/browser/autofill_manager.h" -#include "components/autofill/core/browser/form_structure.h" -#include "components/autofill/core/common/autofill_switches.h" #include "content/public/browser/navigation_handle.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/web_contents.h" namespace autofill { +namespace { + +std::unique_ptr<AutofillDriver> CreateDriver( + content::RenderFrameHost* render_frame_host, + AutofillClient* client, + const std::string& app_locale, + AutofillManager::AutofillDownloadManagerState enable_download_manager) { + return base::MakeUnique<ContentAutofillDriver>( + render_frame_host, client, app_locale, enable_download_manager); +} + +} // namespace + const char ContentAutofillDriverFactory:: kContentAutofillDriverFactoryWebContentsUserDataKey[] = "web_contents_autofill_driver_factory"; @@ -83,48 +94,43 @@ ContentAutofillDriverFactory::ContentAutofillDriverFactory( AutofillClient* client, const std::string& app_locale, AutofillManager::AutofillDownloadManagerState enable_download_manager) - : content::WebContentsObserver(web_contents), - client_(client), + : AutofillDriverFactory(client), + content::WebContentsObserver(web_contents), app_locale_(app_locale), enable_download_manager_(enable_download_manager) {} ContentAutofillDriver* ContentAutofillDriverFactory::DriverForFrame( content::RenderFrameHost* render_frame_host) { - auto mapping = frame_driver_map_.find(render_frame_host); - return mapping == frame_driver_map_.end() ? nullptr : mapping->second.get(); + // This cast is safe because AutofillDriverFactory::AddForKey is protected + // and always called with ContentAutofillDriver instances within + // ContentAutofillDriverFactory. + return static_cast<ContentAutofillDriver*>(DriverForKey(render_frame_host)); } void ContentAutofillDriverFactory::RenderFrameCreated( content::RenderFrameHost* render_frame_host) { - auto insertion_result = - frame_driver_map_.insert(std::make_pair(render_frame_host, nullptr)); - // This is called twice for the main frame. - if (insertion_result.second) { // This was the first time. - insertion_result.first->second = base::MakeUnique<ContentAutofillDriver>( - render_frame_host, client_, app_locale_, enable_download_manager_); - } + AddForKey(render_frame_host, + base::Bind(CreateDriver, render_frame_host, client(), app_locale_, + enable_download_manager_)); } void ContentAutofillDriverFactory::RenderFrameDeleted( content::RenderFrameHost* render_frame_host) { - frame_driver_map_.erase(render_frame_host); -} - -void ContentAutofillDriverFactory::DidNavigateAnyFrame( - content::RenderFrameHost* render_frame_host, - const content::LoadCommittedDetails& details, - const content::FrameNavigateParams& params) { - frame_driver_map_[render_frame_host]->DidNavigateFrame(details, params); + DeleteForKey(render_frame_host); } void ContentAutofillDriverFactory::DidFinishNavigation( content::NavigationHandle* navigation_handle) { - if (navigation_handle->HasCommitted()) - client_->HideAutofillPopup(); + if (!navigation_handle->HasCommitted()) + return; + + NavigationFinished(); + DriverForFrame(navigation_handle->GetRenderFrameHost()) + ->DidNavigateFrame(navigation_handle); } void ContentAutofillDriverFactory::WasHidden() { - client_->HideAutofillPopup(); + TabHidden(); } } // namespace autofill 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 2781ee4c1cf..df2d8bcaf66 100644 --- a/chromium/components/autofill/content/browser/content_autofill_driver_factory.h +++ b/chromium/components/autofill/content/browser/content_autofill_driver_factory.h @@ -5,12 +5,11 @@ #ifndef COMPONENTS_AUTOFILL_CONTENT_BROWSER_CONTENT_AUTOFILL_DRIVER_FACTORY_H_ #define COMPONENTS_AUTOFILL_CONTENT_BROWSER_CONTENT_AUTOFILL_DRIVER_FACTORY_H_ -#include <map> -#include <memory> #include <string> #include "base/supports_user_data.h" #include "components/autofill/content/common/autofill_driver.mojom.h" +#include "components/autofill/core/browser/autofill_driver_factory.h" #include "components/autofill/core/browser/autofill_manager.h" #include "content/public/browser/web_contents_observer.h" @@ -24,7 +23,8 @@ class ContentAutofillDriver; // Manages lifetime of ContentAutofillDriver. One Factory per WebContents // creates one Driver per RenderFrame. -class ContentAutofillDriverFactory : public content::WebContentsObserver, +class ContentAutofillDriverFactory : public AutofillDriverFactory, + public content::WebContentsObserver, public base::SupportsUserData::Data { public: ~ContentAutofillDriverFactory() override; @@ -47,30 +47,21 @@ class ContentAutofillDriverFactory : public content::WebContentsObserver, // content::WebContentsObserver: void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override; void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override; - void DidNavigateAnyFrame( - content::RenderFrameHost* render_frame_host, - const content::LoadCommittedDetails& details, - const content::FrameNavigateParams& params) override; void DidFinishNavigation( content::NavigationHandle* navigation_handle) override; void WasHidden() override; static const char kContentAutofillDriverFactoryWebContentsUserDataKey[]; - protected: + private: ContentAutofillDriverFactory( content::WebContents* web_contents, AutofillClient* client, const std::string& app_locale, AutofillManager::AutofillDownloadManagerState enable_download_manager); - private: - AutofillClient* client_; std::string app_locale_; AutofillManager::AutofillDownloadManagerState enable_download_manager_; - - std::map<content::RenderFrameHost*, std::unique_ptr<ContentAutofillDriver>> - frame_driver_map_; }; } // namespace autofill 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 3dbbd572f3c..9d7fcb2a767 100644 --- a/chromium/components/autofill/content/browser/content_autofill_driver_unittest.cc +++ b/chromium/components/autofill/content/browser/content_autofill_driver_unittest.cc @@ -21,8 +21,8 @@ #include "components/autofill/core/common/autofill_switches.h" #include "components/autofill/core/common/form_data_predictions.h" #include "content/public/browser/browser_context.h" -#include "content/public/browser/navigation_details.h" #include "content/public/browser/navigation_entry.h" +#include "content/public/browser/navigation_handle.h" #include "content/public/browser/ssl_status.h" #include "content/public/browser/storage_partition.h" #include "content/public/browser/web_contents.h" @@ -264,6 +264,9 @@ class ContentAutofillDriverTest : public content::RenderViewHostTestHarness { public: void SetUp() override { content::RenderViewHostTestHarness::SetUp(); + // This needed to keep the WebContentsObserverSanityChecker checks happy for + // when AppendChild is called. + NavigateAndCommit(GURL("about:blank")); test_autofill_client_.reset(new MockAutofillClient()); driver_.reset(new TestContentAutofillDriver(web_contents()->GetMainFrame(), @@ -284,6 +287,18 @@ class ContentAutofillDriverTest : public content::RenderViewHostTestHarness { content::RenderViewHostTestHarness::TearDown(); } + void Navigate(bool main_frame) { + content::RenderFrameHost* rfh = main_rfh(); + content::RenderFrameHostTester* rfh_tester = + content::RenderFrameHostTester::For(rfh); + if (!main_frame) + rfh = rfh_tester->AppendChild("subframe"); + std::unique_ptr<content::NavigationHandle> navigation_handle = + content::NavigationHandle::CreateNavigationHandleForTesting( + GURL(), rfh, true); + driver_->DidNavigateFrame(navigation_handle.get()); + } + protected: std::unique_ptr<MockAutofillClient> test_autofill_client_; std::unique_ptr<TestContentAutofillDriver> driver_; @@ -302,21 +317,12 @@ TEST_F(ContentAutofillDriverTest, GetURLRequestContext) { TEST_F(ContentAutofillDriverTest, NavigatedToDifferentPage) { EXPECT_CALL(*driver_->mock_autofill_manager(), Reset()); - content::LoadCommittedDetails details = content::LoadCommittedDetails(); - details.is_main_frame = true; - details.is_in_page = false; - ASSERT_TRUE(details.is_navigation_to_different_page()); - content::FrameNavigateParams params = content::FrameNavigateParams(); - driver_->DidNavigateFrame(details, params); + Navigate(true); } TEST_F(ContentAutofillDriverTest, NavigatedWithinSamePage) { EXPECT_CALL(*driver_->mock_autofill_manager(), Reset()).Times(0); - content::LoadCommittedDetails details = content::LoadCommittedDetails(); - details.is_main_frame = false; - ASSERT_TRUE(!details.is_navigation_to_different_page()); - content::FrameNavigateParams params = content::FrameNavigateParams(); - driver_->DidNavigateFrame(details, params); + Navigate(false); } TEST_F(ContentAutofillDriverTest, FormDataSentToRenderer_FillForm) { diff --git a/chromium/components/autofill/content/common/autofill_types.mojom b/chromium/components/autofill/content/common/autofill_types.mojom index ec56870ac77..0ff43e1976a 100644 --- a/chromium/components/autofill/content/common/autofill_types.mojom +++ b/chromium/components/autofill/content/common/autofill_types.mojom @@ -64,6 +64,7 @@ enum PasswordFormFieldPredictionType { struct FormFieldData { string label; string name; + string id; string value; string form_control_type; string autocomplete_attribute; @@ -166,6 +167,7 @@ struct PasswordForm { string new_password_value; bool new_password_value_is_default; bool new_password_marked_by_site; + string confirmation_password_element; bool preferred; mojo.common.mojom.Time date_created; mojo.common.mojom.Time date_synced; 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 c536b4abd48..1b3d8ab6214 100644 --- a/chromium/components/autofill/content/common/autofill_types_struct_traits.cc +++ b/chromium/components/autofill/content/common/autofill_types_struct_traits.cc @@ -295,6 +295,8 @@ bool StructTraits<mojom::FormFieldDataDataView, FormFieldData>::Read( return false; if (!data.ReadName(&out->name)) return false; + if (!data.ReadId(&out->id)) + return false; if (!data.ReadValue(&out->value)) return false; @@ -504,6 +506,11 @@ bool StructTraits<mojom::PasswordFormDataView, PasswordForm>::Read( out->new_password_value_is_default = data.new_password_value_is_default(); out->new_password_marked_by_site = data.new_password_marked_by_site(); + + if (!data.ReadConfirmationPasswordElement( + &out->confirmation_password_element)) + return false; + out->preferred = data.preferred(); if (!data.ReadDateCreated(&out->date_created) || 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 c623aa50b30..f5156bb888d 100644 --- a/chromium/components/autofill/content/common/autofill_types_struct_traits.h +++ b/chromium/components/autofill/content/common/autofill_types_struct_traits.h @@ -96,6 +96,10 @@ struct StructTraits<autofill::mojom::FormFieldDataDataView, return r.name; } + static const base::string16& id(const autofill::FormFieldData& r) { + return r.id; + } + static const base::string16& value(const autofill::FormFieldData& r) { return r.value; } @@ -450,6 +454,11 @@ struct StructTraits<autofill::mojom::PasswordFormDataView, return r.new_password_marked_by_site; } + static const base::string16& confirmation_password_element( + const autofill::PasswordForm& r) { + return r.confirmation_password_element; + } + static bool preferred(const autofill::PasswordForm& r) { return r.preferred; } static const base::Time& date_created(const autofill::PasswordForm& r) { diff --git a/chromium/components/autofill/content/common/autofill_types_struct_traits_unittest.cc b/chromium/components/autofill/content/common/autofill_types_struct_traits_unittest.cc index 2da13f212b1..f4ab58b1278 100644 --- a/chromium/components/autofill/content/common/autofill_types_struct_traits_unittest.cc +++ b/chromium/components/autofill/content/common/autofill_types_struct_traits_unittest.cc @@ -92,6 +92,7 @@ void CreateTestPasswordForm(PasswordForm* form) { form->new_password_value = base::ASCIIToUTF16("new_password_value"); form->new_password_value_is_default = false; form->new_password_marked_by_site = false; + form->new_password_element = base::ASCIIToUTF16("confirmation_password"); form->preferred = false; form->date_created = base::Time::Now(); form->date_synced = base::Time::Now(); @@ -327,6 +328,7 @@ TEST_F(AutofillTypeTraitsTestImpl, PassFormFieldData) { test::CreateTestSelectField("TestLabel", "TestName", "TestValue", kOptions, kOptions, 4, &input); // Set other attributes to check if they are passed correctly. + input.id = base::ASCIIToUTF16("id"); input.autocomplete_attribute = "on"; input.placeholder = base::ASCIIToUTF16("placeholder"); input.css_classes = base::ASCIIToUTF16("class1"); diff --git a/chromium/components/autofill/content/renderer/autofill_agent.cc b/chromium/components/autofill/content/renderer/autofill_agent.cc index 0aa8a423a75..c655255b936 100644 --- a/chromium/components/autofill/content/renderer/autofill_agent.cc +++ b/chromium/components/autofill/content/renderer/autofill_agent.cc @@ -43,7 +43,7 @@ #include "net/cert/cert_status_flags.h" #include "services/service_manager/public/cpp/interface_provider.h" #include "services/service_manager/public/cpp/interface_registry.h" -#include "third_party/WebKit/public/platform/WebInputEvent.h" +#include "third_party/WebKit/public/platform/WebKeyboardEvent.h" #include "third_party/WebKit/public/platform/WebURLRequest.h" #include "third_party/WebKit/public/web/WebConsoleMessage.h" #include "third_party/WebKit/public/web/WebDataSource.h" @@ -109,9 +109,9 @@ void GetDataListSuggestions(const WebInputElement& element, std::vector<base::string16>* values, std::vector<base::string16>* labels) { for (const auto& option : element.filteredDataListOptions()) { - values->push_back(option.value()); + values->push_back(option.value().utf16()); if (option.value() != option.label()) - labels->push_back(option.label()); + labels->push_back(option.label().utf16()); else labels->push_back(base::string16()); } @@ -435,8 +435,8 @@ void AutofillAgent::DoAcceptDataListSuggestion( // the suggestion. if (input_element->isMultiple() && input_element->isEmailField()) { std::vector<base::string16> parts = base::SplitString( - base::StringPiece16(input_element->editingValue()), - base::ASCIIToUTF16(","), base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); + input_element->editingValue().utf16(), base::ASCIIToUTF16(","), + base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); if (parts.size() == 0) parts.push_back(base::string16()); @@ -538,8 +538,9 @@ void AutofillAgent::FillPasswordSuggestion(const base::string16& username, void AutofillAgent::PreviewPasswordSuggestion(const base::string16& username, const base::string16& password) { - bool handled = - password_autofill_agent_->PreviewSuggestion(element_, username, password); + bool handled = password_autofill_agent_->PreviewSuggestion( + element_, blink::WebString::fromUTF16(username), + blink::WebString::fromUTF16(password)); DCHECK(handled); } @@ -724,15 +725,18 @@ void AutofillAgent::QueryAutofillSuggestions( void AutofillAgent::DoFillFieldWithValue(const base::string16& value, WebInputElement* node) { base::AutoReset<bool> auto_reset(&ignore_text_changes_, true); - node->setEditingValue(value.substr(0, node->maxLength())); + node->setEditingValue( + blink::WebString::fromUTF16(value.substr(0, node->maxLength()))); } void AutofillAgent::DoPreviewFieldWithValue(const base::string16& value, WebInputElement* node) { was_query_node_autofilled_ = element_.isAutofilled(); - node->setSuggestedValue(value.substr(0, node->maxLength())); + node->setSuggestedValue( + blink::WebString::fromUTF16(value.substr(0, node->maxLength()))); node->setAutofilled(true); - form_util::PreviewSuggestion(node->suggestedValue(), node->value(), node); + form_util::PreviewSuggestion(node->suggestedValue().utf16(), + node->value().utf16(), node); } void AutofillAgent::ProcessForms() { diff --git a/chromium/components/autofill/content/renderer/form_autofill_util.cc b/chromium/components/autofill/content/renderer/form_autofill_util.cc index 8d690c2446a..9677f8c184a 100644 --- a/chromium/components/autofill/content/renderer/form_autofill_util.cc +++ b/chromium/components/autofill/content/renderer/form_autofill_util.cc @@ -102,13 +102,6 @@ bool HasTagName(const WebNode& node, const blink::WebString& tag) { return node.isElementNode() && node.toConst<WebElement>().hasHTMLTagName(tag); } -bool IsAutofillableElement(const WebFormControlElement& element) { - const WebInputElement* input_element = toWebInputElement(&element); - return IsAutofillableInputElement(input_element) || - IsSelectElement(element) || - IsTextAreaElement(element); -} - bool IsElementInControlElementSet( const WebElement& element, const std::vector<WebFormControlElement>& control_elements) { @@ -158,7 +151,7 @@ size_t CalculateTableCellColumnSpan(const WebElement& element) { size_t span = 1; if (element.hasAttribute("colspan")) { - base::string16 colspan = element.getAttribute("colspan"); + base::string16 colspan = element.getAttribute("colspan").utf16(); // Do not check return value to accept imperfect conversions. base::StringToSizeT(colspan, &span); // Handle overflow. @@ -237,7 +230,7 @@ base::string16 FindChildTextInner(const WebNode& node, } // Extract the text exactly at this node. - base::string16 node_text = node.nodeValue(); + base::string16 node_text = node.nodeValue().utf16(); // Recursively compute the children's text. // Preserve inter-element whitespace separation. @@ -262,7 +255,7 @@ base::string16 FindChildTextWithIgnoreList( const WebNode& node, const std::set<WebNode>& divs_to_skip) { if (node.isTextNode()) - return node.nodeValue(); + return node.nodeValue().utf16(); WebNode child = node.firstChild(); @@ -370,7 +363,7 @@ base::string16 InferLabelFromNext(const WebFormControlElement& element) { base::string16 InferLabelFromPlaceholder(const WebFormControlElement& element) { CR_DEFINE_STATIC_LOCAL(WebString, kPlaceholder, ("placeholder")); if (element.hasAttribute(kPlaceholder)) - return element.getAttribute(kPlaceholder); + return element.getAttribute(kPlaceholder).utf16(); return base::string16(); } @@ -382,7 +375,7 @@ base::string16 InferLabelFromValueAttr(const WebFormControlElement& element) { CR_DEFINE_STATIC_LOCAL(WebString, kValue, ("value")); if (element.hasAttribute(kValue) && element.getAttribute(kValue) == element.value()) { - return element.getAttribute(kValue); + return element.getAttribute(kValue).utf16(); } return base::string16(); @@ -766,8 +759,8 @@ void GetOptionStringsFromElement(const WebSelectElement& select_element, for (size_t i = 0; i < list_items.size(); ++i) { if (IsOptionElement(list_items[i])) { const WebOptionElement option = list_items[i].toConst<WebOptionElement>(); - option_values->push_back(option.value()); - option_contents->push_back(option.text()); + option_values->push_back(option.value().utf16()); + option_contents->push_back(option.text().utf16()); } } } @@ -800,7 +793,7 @@ void ForEachMatchingFormFieldCommon( for (size_t i = 0; i < control_elements->size(); ++i) { WebFormControlElement* element = &(*control_elements)[i]; - if (base::string16(element->nameForAutofill()) != data.fields[i].name) { + if (element->nameForAutofill().utf16() != data.fields[i].name) { // This case should be reachable only for pathological websites, which // rename form fields while the user is interacting with the Autofill // popup. I (isherman) am not aware of any such websites, and so am @@ -901,7 +894,7 @@ void FillFormField(const FormFieldData& data, // returns the default maxlength value. TruncateString(&value, input_element->maxLength()); } - field->setAutofillValue(value); + field->setAutofillValue(blink::WebString::fromUTF16(value)); } // Setting the form might trigger JavaScript, which is capable of // destroying the frame. @@ -939,18 +932,19 @@ void PreviewFormField(const FormFieldData& data, if (IsTextInput(input_element) || IsMonthInput(input_element)) { // If the maxlength attribute contains a negative value, maxLength() // returns the default maxlength value. - input_element->setSuggestedValue( - data.value.substr(0, input_element->maxLength())); + input_element->setSuggestedValue(blink::WebString::fromUTF16( + data.value.substr(0, input_element->maxLength()))); input_element->setAutofilled(true); } else if (IsTextAreaElement(*field) || IsSelectElement(*field)) { - field->setSuggestedValue(data.value); + field->setSuggestedValue(blink::WebString::fromUTF16(data.value)); field->setAutofilled(true); } if (is_initiating_node && (IsTextInput(input_element) || IsTextAreaElement(*field))) { // Select the part of the text that the user didn't type. - PreviewSuggestion(field->suggestedValue(), field->value(), field); + PreviewSuggestion(field->suggestedValue().utf16(), field->value().utf16(), + field); } } @@ -1017,7 +1011,7 @@ void MatchLabelsAndFields( if (control.isNull()) { // Sometimes site authors will incorrectly specify the corresponding // field element's name rather than its id, so we compensate here. - base::string16 element_name = label.getAttribute(kFor); + base::string16 element_name = label.getAttribute(kFor).utf16(); if (element_name.empty()) continue; // Look through the list for elements with this name. There can actually @@ -1318,11 +1312,17 @@ bool IsAutofillableInputElement(const WebInputElement* element) { IsCheckableElement(element); } +bool IsAutofillableElement(const WebFormControlElement& element) { + const WebInputElement* input_element = toWebInputElement(&element); + return IsAutofillableInputElement(input_element) || + IsSelectElement(element) || IsTextAreaElement(element); +} + const base::string16 GetFormIdentifier(const WebFormElement& form) { - base::string16 identifier = form.name(); + base::string16 identifier = form.name().utf16(); CR_DEFINE_STATIC_LOCAL(WebString, kId, ("id")); if (identifier.empty()) - identifier = form.getAttribute(kId); + identifier = form.getAttribute(kId).utf16(); return identifier; } @@ -1366,14 +1366,18 @@ void WebFormControlElementToFormField( DCHECK(field); DCHECK(!element.isNull()); CR_DEFINE_STATIC_LOCAL(WebString, kAutocomplete, ("autocomplete")); + CR_DEFINE_STATIC_LOCAL(WebString, kId, ("id")); CR_DEFINE_STATIC_LOCAL(WebString, kRole, ("role")); CR_DEFINE_STATIC_LOCAL(WebString, kPlaceholder, ("placeholder")); CR_DEFINE_STATIC_LOCAL(WebString, kClass, ("class")); - // The label is not officially part of a WebFormControlElement; however, the - // labels for all form control elements are scraped from the DOM and set in - // WebFormElementToFormData. - field->name = element.nameForAutofill(); + // Save both id and name attributes, if present. If there is only one of them, + // it will be saved to |name|. See HTMLFormControlElement::nameForAutofill. + field->name = element.nameForAutofill().utf16(); + base::string16 id = element.getAttribute(kId).utf16(); + if (id != field->name) + field->id = id; + field->form_control_type = element.formControlType().utf8(); field->autocomplete_attribute = element.getAttribute(kAutocomplete).utf8(); if (field->autocomplete_attribute.size() > kMaxDataLength) { @@ -1382,13 +1386,13 @@ void WebFormControlElementToFormField( // attribute was present. field->autocomplete_attribute = "x-max-data-length-exceeded"; } - if (base::LowerCaseEqualsASCII( - base::StringPiece16(element.getAttribute(kRole)), "presentation")) + if (base::LowerCaseEqualsASCII(element.getAttribute(kRole).utf16(), + "presentation")) field->role = FormFieldData::ROLE_ATTRIBUTE_PRESENTATION; - field->placeholder = element.getAttribute(kPlaceholder); + field->placeholder = element.getAttribute(kPlaceholder).utf16(); if (element.hasAttribute(kClass)) - field->css_classes = element.getAttribute(kClass); + field->css_classes = element.getAttribute(kClass).utf16(); if (field_value_and_properties_map) { FieldValueAndPropertiesMaskMap::const_iterator it = @@ -1439,7 +1443,7 @@ void WebFormControlElementToFormField( if (!(extract_mask & EXTRACT_VALUE)) return; - base::string16 value = element.value(); + base::string16 value = element.value().utf16(); if (IsSelectElement(element) && (extract_mask & EXTRACT_OPTION_TEXT)) { const WebSelectElement select_element = element.toConst<WebSelectElement>(); @@ -1449,8 +1453,8 @@ void WebFormControlElementToFormField( if (IsOptionElement(list_items[i])) { const WebOptionElement option_element = list_items[i].toConst<WebOptionElement>(); - if (option_element.value() == value) { - value = option_element.text(); + if (option_element.value().utf16() == value) { + value = option_element.text().utf16(); break; } } @@ -1550,7 +1554,7 @@ bool UnownedCheckoutFormElementsAndFieldSetsToFormData( // A potential problem is that this only checks document.title(), but should // actually check the main frame's title. Thus it may make bad decisions for // iframes. - base::string16 title(base::ToLowerASCII(base::string16(document.title()))); + base::string16 title(base::ToLowerASCII(document.title().utf16())); // Don't check the path for url's without a standard format path component, // such as data:. diff --git a/chromium/components/autofill/content/renderer/form_autofill_util.h b/chromium/components/autofill/content/renderer/form_autofill_util.h index d268c7c5a59..ccf29157086 100644 --- a/chromium/components/autofill/content/renderer/form_autofill_util.h +++ b/chromium/components/autofill/content/renderer/form_autofill_util.h @@ -127,6 +127,10 @@ bool IsCheckableElement(const blink::WebInputElement* element); // autofilled. {Text, Radiobutton, Checkbox}. bool IsAutofillableInputElement(const blink::WebInputElement* element); +// Returns true if |element| is one of the element types that can be autofilled. +// {Text, Radiobutton, Checkbox, Select, TextArea}. +bool IsAutofillableElement(const blink::WebFormControlElement& element); + // True if this node takes up space in the layout, ie. this node or a descendant // has a non-empty bounding bounding client rect. // diff --git a/chromium/components/autofill/content/renderer/form_cache.cc b/chromium/components/autofill/content/renderer/form_cache.cc index 925379d1915..03cb04f47cd 100644 --- a/chromium/components/autofill/content/renderer/form_cache.cc +++ b/chromium/components/autofill/content/renderer/form_cache.cc @@ -11,7 +11,7 @@ #include "components/autofill/content/renderer/form_autofill_util.h" #include "components/autofill/core/common/autofill_constants.h" #include "components/autofill/core/common/form_data_predictions.h" -#include "grit/components_strings.h" +#include "components/strings/grit/components_strings.h" #include "third_party/WebKit/public/platform/WebString.h" #include "third_party/WebKit/public/platform/WebVector.h" #include "third_party/WebKit/public/web/WebConsoleMessage.h" @@ -41,8 +41,7 @@ namespace { void LogDeprecationMessages(const WebFormControlElement& element) { std::string autocomplete_attribute = - base::UTF16ToUTF8(base::StringPiece16( - element.getAttribute("autocomplete"))); + element.getAttribute("autocomplete").utf8(); static const char* const deprecated[] = { "region", "locality" }; for (const char* str : deprecated) { @@ -51,8 +50,7 @@ void LogDeprecationMessages(const WebFormControlElement& element) { std::string msg = std::string("autocomplete='") + str + "' is deprecated and will soon be ignored. See http://goo.gl/YjeSsW"; WebConsoleMessage console_message = WebConsoleMessage( - WebConsoleMessage::LevelWarning, - WebString(base::ASCIIToUTF16(msg))); + WebConsoleMessage::LevelWarning, WebString::fromASCII(msg)); element.document().frame()->addMessageToConsole(console_message); } } @@ -212,7 +210,7 @@ bool FormCache::ClearFormWithElement(const WebFormControlElement& element) { WebInputElement* input_element = toWebInputElement(&control_element); if (form_util::IsTextInput(input_element) || form_util::IsMonthInput(input_element)) { - input_element->setValue(base::string16(), true); + input_element->setValue(blink::WebString(), true); // Clearing the value in the focused node (above) can cause selection // to be lost. We force selection range to restore the text cursor. @@ -221,15 +219,16 @@ bool FormCache::ClearFormWithElement(const WebFormControlElement& element) { input_element->setSelectionRange(length, length); } } else if (form_util::IsTextAreaElement(control_element)) { - control_element.setValue(base::string16(), true); + control_element.setValue(blink::WebString(), true); } else if (form_util::IsSelectElement(control_element)) { WebSelectElement select_element = control_element.to<WebSelectElement>(); std::map<const WebSelectElement, base::string16>::const_iterator initial_value_iter = initial_select_values_.find(select_element); if (initial_value_iter != initial_select_values_.end() && - select_element.value() != initial_value_iter->second) { - select_element.setValue(initial_value_iter->second, true); + select_element.value().utf16() != initial_value_iter->second) { + select_element.setValue( + blink::WebString::fromUTF16(initial_value_iter->second), true); } } else { WebInputElement input_element = control_element.to<WebInputElement>(); @@ -301,7 +300,7 @@ bool FormCache::ShowPredictions(const FormDataPredictions& form) { WebFormControlElement& element = control_elements[i]; const FormFieldData& field_data = form.data.fields[i]; - if (base::string16(element.nameForAutofill()) != field_data.name) { + if (element.nameForAutofill().utf16() != field_data.name) { // Keep things simple. Don't show predictions for elements whose names // were modified between page load and the server's response to our query. continue; @@ -325,9 +324,10 @@ bool FormCache::ShowPredictions(const FormDataPredictions& form) { replacements.push_back(base::UTF8ToUTF16(form.signature)); const base::string16 title = l10n_util::GetStringFUTF16( IDS_AUTOFILL_SHOW_PREDICTIONS_TITLE, replacements, nullptr); - element.setAttribute("title", WebString(title)); + element.setAttribute("title", WebString::fromUTF16(title)); - element.setAttribute("autofill-prediction", WebString(overall_type)); + element.setAttribute("autofill-prediction", + WebString::fromUTF16(overall_type)); } return true; @@ -364,7 +364,7 @@ void FormCache::SaveInitialValues( const WebSelectElement select_element = element.toConst<WebSelectElement>(); initial_select_values_.insert( - std::make_pair(select_element, select_element.value())); + std::make_pair(select_element, select_element.value().utf16())); } else { const WebInputElement* input_element = toWebInputElement(&element); if (form_util::IsCheckableElement(input_element)) { diff --git a/chromium/components/autofill/content/renderer/form_classifier.cc b/chromium/components/autofill/content/renderer/form_classifier.cc index 381dc79985b..4a4ea3e35c4 100644 --- a/chromium/components/autofill/content/renderer/form_classifier.cc +++ b/chromium/components/autofill/content/renderer/form_classifier.cc @@ -257,7 +257,7 @@ bool ClassifyFormAndFindGenerationField(const blink::WebFormElement& form, else password_creation_field = passwords[0]; - *generation_field = password_creation_field.nameForAutofill(); + *generation_field = password_creation_field.nameForAutofill().utf16(); return true; } return false; diff --git a/chromium/components/autofill/content/renderer/password_autofill_agent.cc b/chromium/components/autofill/content/renderer/password_autofill_agent.cc index b17f17f0de1..abac9479d63 100644 --- a/chromium/components/autofill/content/renderer/password_autofill_agent.cc +++ b/chromium/components/autofill/content/renderer/password_autofill_agent.cc @@ -17,6 +17,7 @@ #include "base/memory/ptr_util.h" #include "base/message_loop/message_loop.h" #include "base/metrics/histogram_macros.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/threading/thread_task_runner_handle.h" @@ -169,7 +170,7 @@ bool FindFormInputElement( base::string16 field_name = FieldName(field, ambiguous_or_empty_names); for (const blink::WebFormControlElement& control_element : control_elements) { if (!ambiguous_or_empty_names && - control_element.nameForAutofill() != field_name) { + control_element.nameForAutofill().utf16() != field_name) { continue; } @@ -401,7 +402,7 @@ bool FillUserNameAndPassword( base::string16 current_username; if (!username_element->isNull()) { - current_username = username_element->value(); + current_username = username_element->value().utf16(); } // username and password will contain the match found if any. @@ -455,7 +456,7 @@ bool FillUserNameAndPassword( if (!username_element->isNull() && IsElementAutocompletable(*username_element)) { // TODO(crbug.com/507714): Why not setSuggestedValue? - username_element->setValue(username, true); + username_element->setValue(blink::WebString::fromUTF16(username), true); UpdateFieldValueAndPropertiesMaskMap(*username_element, &username, FieldPropertiesFlags::AUTOFILLED, field_value_and_properties_map); @@ -475,7 +476,7 @@ bool FillUserNameAndPassword( // Wait to fill in the password until a user gesture occurs. This is to make // sure that we do not fill in the DOM with a password until we believe the // user is intentionally interacting with the page. - password_element->setSuggestedValue(password); + password_element->setSuggestedValue(blink::WebString::fromUTF16(password)); UpdateFieldValueAndPropertiesMaskMap(*password_element, &password, FieldPropertiesFlags::AUTOFILLED, field_value_and_properties_map); @@ -550,7 +551,8 @@ bool FillFormOnPasswordReceived( if (form_has_fillable_username && username_element.value().isEmpty()) { // TODO(tkent): Check maxlength and pattern. - username_element.setValue(fill_data.username_field.value, true); + username_element.setValue( + blink::WebString::fromUTF16(fill_data.username_field.value), true); } // Fill if we have an exact match for the username. Note that this sets @@ -561,6 +563,39 @@ bool FillFormOnPasswordReceived( field_value_and_properties_map, registration_callback, logger); } +// Annotate |forms| with form and field signatures as HTML attributes. +void AnnotateFormsWithSignatures( + blink::WebVector<blink::WebFormElement> forms) { + for (blink::WebFormElement form : forms) { + std::unique_ptr<PasswordForm> password_form( + CreatePasswordFormFromWebForm(form, nullptr, nullptr)); + if (password_form) { + form.setAttribute( + blink::WebString::fromASCII(kDebugAttributeForFormSignature), + blink::WebString::fromUTF8(base::Uint64ToString( + CalculateFormSignature(password_form->form_data)))); + + std::vector<blink::WebFormControlElement> control_elements = + form_util::ExtractAutofillableElementsInForm(form); + + if (control_elements.size() != password_form->form_data.fields.size()) + return; + + for (size_t i = 0; i < control_elements.size(); ++i) { + blink::WebFormControlElement control_element = control_elements[i]; + + const FormFieldData& field = password_form->form_data.fields[i]; + if (field.name != control_element.nameForAutofill().utf16()) + continue; + control_element.setAttribute( + blink::WebString::fromASCII(kDebugAttributeForFieldSignature), + blink::WebString::fromUTF8( + base::Uint64ToString(CalculateFieldSignatureForField(field)))); + } + } + } +} + } // namespace //////////////////////////////////////////////////////////////////////////////// @@ -646,7 +681,7 @@ void PasswordAutofillAgent::UpdateStateForTextChange( blink::WebInputElement mutable_element = element; // We need a non-const. if (element.isTextField()) { - const base::string16 element_value = element.value(); + const base::string16 element_value = element.value().utf16(); UpdateFieldValueAndPropertiesMaskMap(element, &element_value, FieldPropertiesFlags::USER_TYPED, &field_value_and_properties_map_); @@ -713,14 +748,14 @@ bool PasswordAutofillAgent::FillSuggestion( password_info->password_field = password_element; } else if (!username_element.isNull() && IsElementAutocompletable(username_element)) { - username_element.setValue(blink::WebString(username), true); + username_element.setValue(blink::WebString::fromUTF16(username), true); username_element.setAutofilled(true); UpdateFieldValueAndPropertiesMaskMap(username_element, &username, FieldPropertiesFlags::AUTOFILLED, &field_value_and_properties_map_); } - password_element.setValue(blink::WebString(password), true); + password_element.setValue(blink::WebString::fromUTF16(password), true); password_element.setAutofilled(true); UpdateFieldValueAndPropertiesMaskMap(password_element, &password, FieldPropertiesFlags::AUTOFILLED, @@ -755,12 +790,12 @@ bool PasswordAutofillAgent::PreviewSuggestion( if (!element->isPasswordField() && !username_element.isNull() && IsElementAutocompletable(username_element)) { if (username_query_prefix_.empty()) - username_query_prefix_ = username_element.value(); + username_query_prefix_ = username_element.value().utf16(); was_username_autofilled_ = username_element.isAutofilled(); username_element.setSuggestedValue(username); username_element.setAutofilled(true); - form_util::PreviewSuggestion(username_element.suggestedValue(), + form_util::PreviewSuggestion(username_element.suggestedValue().utf16(), username_query_prefix_, &username_element); } was_password_autofilled_ = password_element.isAutofilled(); @@ -836,6 +871,46 @@ bool PasswordAutofillAgent::FindPasswordInfoForElement( return true; } +bool PasswordAutofillAgent::ShouldShowNotSecureWarning( + const blink::WebInputElement& element) { + // Do not show a warning if the feature is disabled or the context is secure. + if (!security_state::IsHttpWarningInFormEnabled() || + content::IsOriginSecure( + url::Origin(render_frame()->GetWebFrame()->top()->getSecurityOrigin()) + .GetURL())) + return false; + + // Show the warning on all Password inputs. + // Note: A site may use a Password field to collect a CVV or a Credit Card + // number, but showing a slightly misleading warning here is better than + // showing no warning at all. + if (element.isPasswordField()) + return true; + + // If a field declares itself a username input, show the warning. + if (HasAutocompleteAttributeValue(element, "username")) + return true; + + // Otherwise, analyze the form and return true if this input element seems + // to be the username field. + std::unique_ptr<PasswordForm> password_form; + if (element.form().isNull()) { + blink::WebFrame* const element_frame = element.document().frame(); + if (!element_frame) + return false; + + password_form = CreatePasswordFormFromUnownedInputElements( + *element_frame, &field_value_and_properties_map_, &form_predictions_); + } else { + password_form = CreatePasswordFormFromWebForm( + element.form(), &field_value_and_properties_map_, &form_predictions_); + } + + if (!password_form) + return false; + return (password_form->username_element == element.nameForAutofill().utf16()); +} + bool PasswordAutofillAgent::ShowSuggestions( const blink::WebInputElement& element, bool show_all, @@ -846,15 +921,7 @@ bool PasswordAutofillAgent::ShowSuggestions( if (!FindPasswordInfoForElement(element, &username_element, &password_element, &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())) { + if (ShouldShowNotSecureWarning(element)) { autofill_agent_->ShowNotSecureWarning(element); return true; } @@ -983,6 +1050,9 @@ void PasswordAutofillAgent::SendPasswordForms(bool only_visible) { blink::WebVector<blink::WebFormElement> forms; frame->document().forms(forms); + + if (IsShowAutofillSignaturesEnabled()) + AnnotateFormsWithSignatures(forms); if (logger) logger->LogNumber(Logger::STRING_NUMBER_OF_ALL_FORMS, forms.size()); @@ -1165,7 +1235,8 @@ void PasswordAutofillAgent::OnDestruct() { base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this); } -void PasswordAutofillAgent::DidStartProvisionalLoad() { +void PasswordAutofillAgent::DidStartProvisionalLoad( + blink::WebDataSource* data_source) { std::unique_ptr<RendererSavePasswordProgressLogger> logger; if (logging_state_active_) { logger.reset(new RendererSavePasswordProgressLogger( @@ -1184,8 +1255,7 @@ void PasswordAutofillAgent::DidStartProvisionalLoad() { // the user is performing actions outside the page (e.g. typed url, // history navigation). We don't want to trigger saving in these cases. content::DocumentState* document_state = - content::DocumentState::FromDataSource( - navigated_frame->provisionalDataSource()); + content::DocumentState::FromDataSource(data_source); content::NavigationState* navigation_state = document_state->navigation_state(); ui::PageTransition type = navigation_state->GetTransitionType(); @@ -1362,6 +1432,19 @@ void PasswordAutofillAgent::GetFillableElementFromFormData( if (elements) elements->push_back(main_element); } + + // This is a fallback, if for some reasons elements for filling were not found + // (for example because they were renamed by JavaScript) then add fill data + // for |web_input_to_password_info_|. When the user clicks on a password + // field which is not a key in |web_input_to_password_info_|, the first + // element from |web_input_to_password_info_| will be used in + // PasswordAutofillAgent::FindPasswordInfoForElement to propose to fill. + if (web_input_to_password_info_.empty()) { + PasswordInfo password_info; + password_info.fill_data = form_data; + password_info.key = key; + web_input_to_password_info_[blink::WebInputElement()] = password_info; + } } void PasswordAutofillAgent::FocusedNodeHasChanged(const blink::WebNode& node) { @@ -1395,7 +1478,7 @@ void PasswordAutofillAgent::FindFocusedPasswordForm( render_frame()->GetWebFrame()->document().focusedElement(); if (!element.isNull() && element.hasHTMLTagName("input")) { blink::WebInputElement input = element.to<blink::WebInputElement>(); - if (input.isPasswordField() && !input.form().isNull()) { + if (input.isPasswordField()) { if (!input.form().isNull()) { password_form = CreatePasswordFormFromWebForm( input.form(), &field_value_and_properties_map_, &form_predictions_); @@ -1405,8 +1488,10 @@ void PasswordAutofillAgent::FindFocusedPasswordForm( &form_predictions_); // Only try to use this form if |input| is one of the password elements // for |password_form|. - if (password_form->password_element != input.nameForAutofill() && - password_form->new_password_element != input.nameForAutofill()) + if (password_form->password_element != + input.nameForAutofill().utf16() && + password_form->new_password_element != + input.nameForAutofill().utf16()) password_form.reset(); } } @@ -1451,10 +1536,9 @@ bool PasswordAutofillAgent::ShowSuggestionPopup( if (show_on_password_field) options |= IS_PASSWORD_FIELD; - base::string16 username_string( - user_input.isPasswordField() - ? base::string16() - : static_cast<base::string16>(user_input.value())); + base::string16 username_string(user_input.isPasswordField() + ? base::string16() + : user_input.value().utf16()); GetPasswordManagerDriver()->ShowPasswordSuggestions( password_info.key, field.text_direction, username_string, options, diff --git a/chromium/components/autofill/content/renderer/password_autofill_agent.h b/chromium/components/autofill/content/renderer/password_autofill_agent.h index ce63053cce8..12b7d03c639 100644 --- a/chromium/components/autofill/content/renderer/password_autofill_agent.h +++ b/chromium/components/autofill/content/renderer/password_autofill_agent.h @@ -30,6 +30,10 @@ class WebSecurityOrigin; namespace autofill { +// Names of HTML attributes to show form and field signatures for debugging. +const char kDebugAttributeForFormSignature[] = "form_signature"; +const char kDebugAttributeForFieldSignature[] = "field_signature"; + class RendererSavePasswordProgressLogger; // This class is responsible for filling password forms. @@ -83,6 +87,10 @@ class PasswordAutofillAgent : public content::RenderFrameObserver, bool DidClearAutofillSelection( const blink::WebFormControlElement& control_element); + // If the form is non-secure, show the "Not Secure" warning on username and + // password input fields. + bool ShouldShowNotSecureWarning(const blink::WebInputElement& element); + // Shows an Autofill popup with username suggestions for |element|. If // |show_all| is |true|, will show all possible suggestions for that element, // otherwise shows suggestions based on current value of |element|. @@ -188,7 +196,7 @@ class PasswordAutofillAgent : public content::RenderFrameObserver, void DidFinishDocumentLoad() override; void DidFinishLoad() override; void FrameDetached() override; - void DidStartProvisionalLoad() override; + void DidStartProvisionalLoad(blink::WebDataSource* data_source) override; void WillCommitProvisionalLoad() override; void DidCommitProvisionalLoad(bool is_new_navigation, bool is_same_page_navigation) override; 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 3465657dd93..3aa8e5b3da2 100644 --- a/chromium/components/autofill/content/renderer/password_form_conversion_utils.cc +++ b/chromium/components/autofill/content/renderer/password_form_conversion_utils.cc @@ -146,11 +146,10 @@ void PopulateSyntheticFormFromWebForm(const WebFormElement& web_form, void ExcludeUsernameFromOtherUsernamesList( const WebInputElement& username_element, std::vector<base::string16>* other_possible_usernames) { - other_possible_usernames->erase( - std::remove(other_possible_usernames->begin(), - other_possible_usernames->end(), - username_element.value()), - other_possible_usernames->end()); + other_possible_usernames->erase(std::remove(other_possible_usernames->begin(), + other_possible_usernames->end(), + username_element.value().utf16()), + other_possible_usernames->end()); } // Helper to determine which password is the main (current) one, and which is @@ -288,7 +287,7 @@ void FindPredictedElements( const FormFieldData& target_field = prediction.first; const PasswordFormFieldPredictionType& type = prediction.second; for (const auto& control_element : autofillable_elements) { - if (control_element.nameForAutofill() == target_field.name) { + if (control_element.nameForAutofill().utf16() == target_field.name) { const WebInputElement* input_element = toWebInputElement(&control_element); @@ -321,7 +320,7 @@ base::LazyInstance<re2::RE2, PasswordSiteUrlLazyInstanceTraits> // Returns the |input_field| name if its non-empty; otherwise a |dummy_name|. base::string16 FieldName(const WebInputElement& input_field, const char dummy_name[]) { - base::string16 field_name = input_field.nameForAutofill(); + base::string16 field_name = input_field.nameForAutofill().utf16(); return field_name.empty() ? base::ASCIIToUTF16(dummy_name) : field_name; } @@ -483,7 +482,7 @@ bool GetPasswordForm( // autofill, not for form identification, and blank autofill entries // are not useful, so we do not collect empty strings. if (!input_element->value().isEmpty()) - other_possible_usernames.push_back(input_element->value()); + other_possible_usernames.push_back(input_element->value().utf16()); } else { // The first element marked with autocomplete='username'. Take the // hint and treat it as the username (overruling the tentative choice @@ -507,7 +506,7 @@ bool GetPasswordForm( if (username_element.isNull()) latest_input_element = *input_element; if (!input_element->value().isEmpty()) - other_possible_usernames.push_back(input_element->value()); + other_possible_usernames.push_back(input_element->value().utf16()); } } } @@ -546,7 +545,7 @@ bool GetPasswordForm( ExcludeUsernameFromOtherUsernamesList(predicted_username_element, &other_possible_usernames); if (!username_element.isNull()) { - other_possible_usernames.push_back(username_element.value()); + other_possible_usernames.push_back(username_element.value().utf16()); } username_element = predicted_username_element; password_form->was_parsed_using_autofill_predictions = true; @@ -555,7 +554,7 @@ bool GetPasswordForm( if (!username_element.isNull()) { password_form->username_element = FieldName(username_element, "anonymous_username"); - base::string16 username_value = username_element.value(); + base::string16 username_value = username_element.value().utf16(); if (FieldHasNonscriptModifiedValue(field_value_and_properties_map, username_element)) { base::string16 typed_username_value = @@ -585,13 +584,14 @@ bool GetPasswordForm( blink::WebString password_value = password.value(); if (FieldHasNonscriptModifiedValue(field_value_and_properties_map, password)) - password_value = *field_value_and_properties_map->at(password).first; - password_form->password_value = password_value; + password_value = blink::WebString::fromUTF16( + *field_value_and_properties_map->at(password).first); + password_form->password_value = password_value.utf16(); } if (!new_password.isNull()) { password_form->new_password_element = FieldName(new_password, "anonymous_new_password"); - password_form->new_password_value = new_password.value(); + password_form->new_password_value = new_password.value().utf16(); password_form->new_password_value_is_default = new_password.getAttribute("value") == new_password.value(); if (HasAutocompleteAttributeValue(new_password, kAutocompleteNewPassword)) @@ -721,7 +721,8 @@ std::unique_ptr<PasswordForm> CreatePasswordFormFromUnownedInputElements( bool HasAutocompleteAttributeValue(const blink::WebInputElement& element, const char* value_in_lowercase) { - base::string16 autocomplete_attribute(element.getAttribute("autocomplete")); + base::string16 autocomplete_attribute( + element.getAttribute("autocomplete").utf16()); std::vector<std::string> tokens = LowercaseAndTokenizeAttributeString( base::UTF16ToUTF8(autocomplete_attribute)); 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 08b1988e158..ead27968a84 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 @@ -186,7 +186,7 @@ class MAYBE_PasswordFormConversionUtilsTest : public content::RenderViewTest { if (input_element->hasAttribute("set-activated-submit")) input_element->setActivatedSubmit(true); if (with_user_input) { - const base::string16 element_value = input_element->value(); + const base::string16 element_value = input_element->value().utf16(); user_input[control_elements[i]] = std::make_pair(base::MakeUnique<base::string16>(element_value), 0U); } diff --git a/chromium/components/autofill/content/renderer/password_generation_agent.cc b/chromium/components/autofill/content/renderer/password_generation_agent.cc index 183b5f068c2..ebca6d4b1cb 100644 --- a/chromium/components/autofill/content/renderer/password_generation_agent.cc +++ b/chromium/components/autofill/content/renderer/password_generation_agent.cc @@ -80,7 +80,7 @@ std::vector<blink::WebInputElement> FindPasswordElementsForGeneration( all_password_elements.begin(), all_password_elements.end(), [&field_signature](const blink::WebInputElement& input) { FieldSignature signature = CalculateFieldSignatureByNameAndType( - input.nameForAutofill(), input.formControlType().utf8()); + input.nameForAutofill().utf16(), input.formControlType().utf8()); return signature == field_signature; }); std::vector<blink::WebInputElement> passwords; @@ -305,7 +305,8 @@ void PasswordGenerationAgent::GeneratedPasswordAccepted( password_generation::LogPasswordGenerationEvent( password_generation::PASSWORD_ACCEPTED); for (auto& password_element : generation_form_data_->password_elements) { - password_element.setValue(password, true /* sendEvents */); + password_element.setValue(blink::WebString::fromUTF16(password), + true /* sendEvents */); // setValue() above may have resulted in JavaScript closing the frame. if (!render_frame()) return; @@ -343,7 +344,7 @@ PasswordGenerationAgent::CreatePasswordFormToPresave() { // TODO(kolos): when we are good in username detection, save username // as well. password_form->username_value = base::string16(); - password_form->password_value = generation_element_.value(); + password_form->password_value = generation_element_.value().utf16(); } return password_form; @@ -515,8 +516,9 @@ void PasswordGenerationAgent::ShowGenerationPopup() { GetPasswordManagerClient()->ShowPasswordGenerationPopup( render_frame()->GetRenderView()->ElementBoundsInWindow( generation_element_), - generation_element_.maxLength(), generation_element_.nameForAutofill(), - is_manually_triggered_, *generation_form_data_->form); + generation_element_.maxLength(), + generation_element_.nameForAutofill().utf16(), is_manually_triggered_, + *generation_form_data_->form); generation_popup_shown_ = true; } @@ -564,7 +566,7 @@ void PasswordGenerationAgent::UserTriggeredGeneratePassword() { password_elements = FindPasswordElementsForGeneration( password_elements, CalculateFieldSignatureByNameAndType( - last_focused_password_element_.nameForAutofill(), + last_focused_password_element_.nameForAutofill().utf16(), last_focused_password_element_.formControlType().utf8())); generation_form_data_.reset(new AccountCreationFormData( make_linked_ptr(password_form.release()), password_elements)); diff --git a/chromium/components/autofill/content/renderer/renderer_save_password_progress_logger.cc b/chromium/components/autofill/content/renderer/renderer_save_password_progress_logger.cc index 9dd724a22c1..bd71dbc0d88 100644 --- a/chromium/components/autofill/content/renderer/renderer_save_password_progress_logger.cc +++ b/chromium/components/autofill/content/renderer/renderer_save_password_progress_logger.cc @@ -25,8 +25,8 @@ void RendererSavePasswordProgressLogger::SendLog(const std::string& log) { void RendererSavePasswordProgressLogger::LogElementName( StringID label, const blink::WebFormControlElement& element) { - LogValue(label, base::StringValue(ScrubElementID( - base::string16(element.nameForAutofill())))); + LogValue(label, base::StringValue( + ScrubElementID((element.nameForAutofill().utf16())))); } } // namespace autofill diff --git a/chromium/components/autofill/core/browser/BUILD.gn b/chromium/components/autofill/core/browser/BUILD.gn index 5281b7de472..4e6332c41f8 100644 --- a/chromium/components/autofill/core/browser/BUILD.gn +++ b/chromium/components/autofill/core/browser/BUILD.gn @@ -28,6 +28,8 @@ static_library("browser") { "autofill_download_manager.cc", "autofill_download_manager.h", "autofill_driver.h", + "autofill_driver_factory.cc", + "autofill_driver_factory.h", "autofill_experiments.cc", "autofill_experiments.h", "autofill_external_delegate.cc", @@ -60,6 +62,8 @@ static_library("browser") { "card_unmask_delegate.h", "contact_info.cc", "contact_info.h", + "country_combobox_model.cc", + "country_combobox_model.h", "country_data.cc", "country_data.h", "country_names.cc", @@ -135,6 +139,9 @@ static_library("browser") { "webdata/autofill_profile_syncable_service.h", "webdata/autofill_table.cc", "webdata/autofill_table.h", + "webdata/autofill_table_encryptor.h", + "webdata/autofill_table_encryptor_factory.cc", + "webdata/autofill_table_encryptor_factory.h", "webdata/autofill_wallet_metadata_syncable_service.cc", "webdata/autofill_wallet_metadata_syncable_service.h", "webdata/autofill_wallet_syncable_service.cc", @@ -146,6 +153,10 @@ static_library("browser") { "webdata/autofill_webdata_service.cc", "webdata/autofill_webdata_service.h", "webdata/autofill_webdata_service_observer.h", + "webdata/system_encryptor.cc", + "webdata/system_encryptor.h", + "webdata/web_data_model_type_controller.cc", + "webdata/web_data_model_type_controller.h", ] if (is_ios) { @@ -189,6 +200,7 @@ static_library("browser") { "//components/signin/core/common", "//components/strings", "//components/sync", + "//components/ukm", "//components/variations/net", "//components/version_info", "//components/webdata/common", @@ -225,6 +237,8 @@ static_library("test_support") { "suggestion_test_helpers.h", "test_autofill_client.cc", "test_autofill_client.h", + "test_autofill_clock.cc", + "test_autofill_clock.h", "test_autofill_driver.cc", "test_autofill_driver.h", "test_autofill_external_delegate.cc", @@ -250,6 +264,8 @@ static_library("test_support") { "//components/rappor:test_support", "//components/signin/core/browser", "//components/signin/core/common", + "//components/ukm", + "//components/ukm:test_support", "//google_apis:test_support", "//skia", "//testing/gtest", @@ -293,6 +309,7 @@ source_set("unit_tests") { "autofill_data_model_unittest.cc", "autofill_data_util_unittest.cc", "autofill_download_manager_unittest.cc", + "autofill_driver_factory_unittest.cc", "autofill_external_delegate_unittest.cc", "autofill_field_unittest.cc", "autofill_ie_toolbar_import_win_unittest.cc", @@ -303,6 +320,7 @@ source_set("unit_tests") { "autofill_profile_unittest.cc", "autofill_type_unittest.cc", "contact_info_unittest.cc", + "country_combobox_model_unittest.cc", "country_names_unittest.cc", "credit_card_field_unittest.cc", "credit_card_unittest.cc", @@ -340,6 +358,7 @@ source_set("unit_tests") { "//base", "//base/test:test_support", "//components/autofill/core/common", + "//components/metrics/proto", "//components/os_crypt", "//components/os_crypt:test_support", "//components/prefs:test_support", @@ -353,7 +372,10 @@ source_set("unit_tests") { "//components/sync", "//components/sync:test_support_driver", "//components/sync:test_support_model", + "//components/ukm", + "//components/ukm:test_support", "//components/variations", + "//components/variations:test_support", "//components/webdata/common", "//components/webdata_services:test_support", "//google_apis", diff --git a/chromium/components/autofill/core/browser/DEPS b/chromium/components/autofill/core/browser/DEPS index a72c3d8b45e..54761f260e5 100644 --- a/chromium/components/autofill/core/browser/DEPS +++ b/chromium/components/autofill/core/browser/DEPS @@ -1,11 +1,14 @@ include_rules = [ "+components/data_use_measurement/core", + "+components/grit/components_scaled_resources.h", "+components/infobars/core", "+components/keyed_service/core", + "+components/metrics", "+components/security_state", "+components/signin/core/browser", "+components/signin/core/common", "+components/sync", + "+components/ukm", "+components/variations", "+components/version_info", "+components/webdata/common", diff --git a/chromium/components/autofill/core/browser/autofill_assistant.cc b/chromium/components/autofill/core/browser/autofill_assistant.cc index 3e463b614bc..02b20bd98bf 100644 --- a/chromium/components/autofill/core/browser/autofill_assistant.cc +++ b/chromium/components/autofill/core/browser/autofill_assistant.cc @@ -55,8 +55,8 @@ void AutofillAssistant::ShowAssistForCreditCard(const CreditCard& card) { void AutofillAssistant::OnUserDidAcceptCreditCardFill(const CreditCard& card) { autofill_manager_->GetOrCreateFullCardRequest()->GetFullCard( - card, AutofillClient::UNMASK_FOR_AUTOFILL, - weak_ptr_factory_.GetWeakPtr()); + card, AutofillClient::UNMASK_FOR_AUTOFILL, weak_ptr_factory_.GetWeakPtr(), + autofill_manager_->GetAsFullCardRequestUIDelegate()); } void AutofillAssistant::OnFullCardRequestSucceeded(const CreditCard& card, diff --git a/chromium/components/autofill/core/browser/autofill_assistant.h b/chromium/components/autofill/core/browser/autofill_assistant.h index fb3d4ae2e74..b383faa6ae9 100644 --- a/chromium/components/autofill/core/browser/autofill_assistant.h +++ b/chromium/components/autofill/core/browser/autofill_assistant.h @@ -21,10 +21,10 @@ class FormStructure; // This class encompasses the triggering rules and the logic for the autofill // assisted filling mechanisms. -class AutofillAssistant : public payments::FullCardRequest::Delegate { +class AutofillAssistant : public payments::FullCardRequest::ResultDelegate { public: explicit AutofillAssistant(AutofillManager* autofill_manager); - ~AutofillAssistant(); + ~AutofillAssistant() override; // Should be called at every page navigation to clear state. void Reset(); @@ -43,7 +43,7 @@ class AutofillAssistant : public payments::FullCardRequest::Delegate { // Called by the infobar delegate when the user accepts the infobar. void OnUserDidAcceptCreditCardFill(const CreditCard& card); - // payments::FullCardRequest::Delegate: + // payments::FullCardRequest::ResultDelegate: void OnFullCardRequestSucceeded(const CreditCard& card, const base::string16& cvc) override; void OnFullCardRequestFailed() override; diff --git a/chromium/components/autofill/core/browser/autofill_client.h b/chromium/components/autofill/core/browser/autofill_client.h index 3960f44fdbb..d36f0026ffc 100644 --- a/chromium/components/autofill/core/browser/autofill_client.h +++ b/chromium/components/autofill/core/browser/autofill_client.h @@ -36,6 +36,10 @@ namespace syncer { class SyncService; } +namespace ukm { +class UkmService; +} + namespace autofill { class AutofillPopupDelegate; @@ -105,6 +109,9 @@ class AutofillClient { // Gets the RapporServiceImpl associated with the client (for metrics). virtual rappor::RapporServiceImpl* GetRapporServiceImpl() = 0; + // Gets the UKM service assiciated with this client (for metrics). + virtual ukm::UkmService* GetUkmService() = 0; + // Causes the Autofill settings UI to be shown. virtual void ShowAutofillSettings() = 0; diff --git a/chromium/components/autofill/core/browser/autofill_credit_card_filling_infobar_delegate_mobile.cc b/chromium/components/autofill/core/browser/autofill_credit_card_filling_infobar_delegate_mobile.cc index c79d465c41d..5f0942c539e 100644 --- a/chromium/components/autofill/core/browser/autofill_credit_card_filling_infobar_delegate_mobile.cc +++ b/chromium/components/autofill/core/browser/autofill_credit_card_filling_infobar_delegate_mobile.cc @@ -7,9 +7,9 @@ #include "base/memory/ptr_util.h" #include "components/autofill/core/browser/credit_card.h" #include "components/autofill/core/common/autofill_constants.h" +#include "components/grit/components_scaled_resources.h" #include "components/infobars/core/infobar_delegate.h" -#include "grit/components_scaled_resources.h" -#include "grit/components_strings.h" +#include "components/strings/grit/components_strings.h" #include "ui/base/l10n/l10n_util.h" namespace autofill { diff --git a/chromium/components/autofill/core/browser/autofill_data_model.cc b/chromium/components/autofill/core/browser/autofill_data_model.cc index 4f42566bcb5..1d36be4f55c 100644 --- a/chromium/components/autofill/core/browser/autofill_data_model.cc +++ b/chromium/components/autofill/core/browser/autofill_data_model.cc @@ -7,6 +7,7 @@ #include <math.h> #include "components/autofill/core/browser/autofill_type.h" +#include "components/autofill/core/common/autofill_clock.h" #include "url/gurl.h" namespace autofill { @@ -16,8 +17,8 @@ AutofillDataModel::AutofillDataModel(const std::string& guid, : guid_(guid), origin_(origin), use_count_(1), - use_date_(base::Time::Now()), - modification_date_(base::Time::Now()) {} + use_date_(AutofillClock::Now()), + modification_date_(AutofillClock::Now()) {} AutofillDataModel::~AutofillDataModel() {} bool AutofillDataModel::IsVerified() const { @@ -27,7 +28,7 @@ bool AutofillDataModel::IsVerified() const { // TODO(crbug.com/629507): Add support for injected mock clock for testing. void AutofillDataModel::RecordUse() { ++use_count_; - use_date_ = base::Time::Now(); + use_date_ = AutofillClock::Now(); } bool AutofillDataModel::CompareFrecency(const AutofillDataModel* other, @@ -50,6 +51,8 @@ double AutofillDataModel::GetFrecencyScore(base::Time time) const { // of the profile and leveraging the properties of the logarithmic function. // DaysSinceLastUse() and |use_count_| are offset because their minimum values // are respectively 0 and 1 but the formula requires at least a value of 2. + // Please update getFrecencyScore in PaymentRequestImpl.java as well if below + // formula needs update. return -log((time - use_date_).InDays() + 2) / log(use_count_ + 1); } diff --git a/chromium/components/autofill/core/browser/autofill_data_util.cc b/chromium/components/autofill/core/browser/autofill_data_util.cc index d931d7b0888..f55ec9068c5 100644 --- a/chromium/components/autofill/core/browser/autofill_data_util.cc +++ b/chromium/components/autofill/core/browser/autofill_data_util.cc @@ -12,6 +12,7 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/field_types.h" +#include "components/grit/components_scaled_resources.h" #include "third_party/icu/source/common/unicode/uscript.h" namespace autofill { @@ -75,12 +76,12 @@ const char* korean_multi_char_surnames[] = { // Returns true if |set| contains |element|, modulo a final period. bool ContainsString(const char* const set[], size_t set_size, - const base::string16& element) { + base::StringPiece16 element) { if (!base::IsStringASCII(element)) return false; - base::string16 trimmed_element; - base::TrimString(element, base::ASCIIToUTF16("."), &trimmed_element); + base::StringPiece16 trimmed_element = + base::TrimString(element, base::ASCIIToUTF16("."), base::TRIM_ALL); for (size_t i = 0; i < set_size; ++i) { if (base::LowerCaseEqualsASCII(trimmed_element, set[i])) @@ -91,21 +92,21 @@ bool ContainsString(const char* const set[], } // Removes common name prefixes from |name_tokens|. -void StripPrefixes(std::vector<base::string16>* name_tokens) { - std::vector<base::string16>::iterator iter = name_tokens->begin(); +void StripPrefixes(std::vector<base::StringPiece16>* name_tokens) { + std::vector<base::StringPiece16>::iterator iter = name_tokens->begin(); while (iter != name_tokens->end()) { if (!ContainsString(name_prefixes, arraysize(name_prefixes), *iter)) break; ++iter; } - std::vector<base::string16> copy_vector; + std::vector<base::StringPiece16> copy_vector; copy_vector.assign(iter, name_tokens->end()); *name_tokens = copy_vector; } // Removes common name suffixes from |name_tokens|. -void StripSuffixes(std::vector<base::string16>* name_tokens) { +void StripSuffixes(std::vector<base::StringPiece16>* name_tokens) { while (!name_tokens->empty()) { if (!ContainsString(name_suffixes, arraysize(name_suffixes), name_tokens->back())) { @@ -156,7 +157,7 @@ bool IsHangulCharacter(UChar32 c) { // Returns true if |name| looks like a Korean name, made up entirely of Hangul // characters or spaces. |name| should already be confirmed to be a CJK name, as // per |IsCJKName()|. -bool IsHangulName(const base::string16& name) { +bool IsHangulName(base::StringPiece16 name) { for (base::i18n::UTF16CharIterator iter(name.data(), name.length()); !iter.end(); iter.Advance()) { UChar32 c = iter.get(); @@ -170,7 +171,7 @@ bool IsHangulName(const base::string16& name) { // Tries to split a Chinese, Japanese, or Korean name into its given name & // surname parts, and puts the result in |parts|. If splitting did not work for // whatever reason, returns false. -bool SplitCJKName(const std::vector<base::string16>& name_tokens, +bool SplitCJKName(const std::vector<base::StringPiece16>& name_tokens, NameParts* parts) { // The convention for CJK languages is to put the surname (last name) first, // and the given name (first name) second. In a continuous text, there is @@ -187,7 +188,7 @@ bool SplitCJKName(const std::vector<base::string16>& name_tokens, // since we don't have a list of Japanese last names. In the Han alphabet, // it might also be difficult for us to differentiate between Chinese & // Japanese names. - const base::string16& name = name_tokens.front(); + const base::StringPiece16& name = name_tokens.front(); const bool is_korean = IsHangulName(name); size_t surname_length = 0; if (is_korean && name.size() > 3) { @@ -204,15 +205,15 @@ bool SplitCJKName(const std::vector<base::string16>& name_tokens, 1, StartsWithAny(name, common_cjk_multi_char_surnames, arraysize(common_cjk_multi_char_surnames))); } - parts->family = name.substr(0, surname_length); - parts->given = name.substr(surname_length); + parts->family = name.substr(0, surname_length).as_string(); + parts->given = name.substr(surname_length).as_string(); return true; } if (name_tokens.size() == 2) { // The user entered a space between the two name parts. This makes our job // easier. Family name first, given name second. - parts->family = name_tokens[0]; - parts->given = name_tokens[1]; + parts->family = name_tokens[0].as_string(); + parts->given = name_tokens[1].as_string(); return true; } // We don't know what to do if there are more than 2 tokens. @@ -221,7 +222,7 @@ bool SplitCJKName(const std::vector<base::string16>& name_tokens, } // namespace -bool IsCJKName(const base::string16& name) { +bool IsCJKName(base::StringPiece16 name) { // The name is considered to be a CJK name if it is only CJK characters, // spaces, and "middle dot" separators, with at least one CJK character, and // no more than 2 words. @@ -254,7 +255,7 @@ bool IsCJKName(const base::string16& name) { return word_count > 0 && word_count < 3; } -NameParts SplitName(const base::string16& name) { +NameParts SplitName(base::StringPiece16 name) { static const base::char16 kWordSeparators[] = { u' ', // ASCII space. u',', // ASCII comma. @@ -263,7 +264,7 @@ NameParts SplitName(const base::string16& name) { u'\u00B7', // 'MIDDLE DOT' (U+00B7). u'\0' // End of string. }; - std::vector<base::string16> name_tokens = base::SplitString( + std::vector<base::StringPiece16> name_tokens = base::SplitStringPiece( name, kWordSeparators, base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY); StripPrefixes(&name_tokens); @@ -284,19 +285,19 @@ NameParts SplitName(const base::string16& name) { if (name_tokens.empty()) { // Bad things have happened; just assume the whole thing is a given name. - parts.given = name; + parts.given = name.as_string(); return parts; } // Only one token, assume given name. if (name_tokens.size() == 1) { - parts.given = name_tokens[0]; + parts.given = name_tokens[0].as_string(); return parts; } // 2 or more tokens. Grab the family, which is the last word plus any // recognizable family prefixes. - std::vector<base::string16> reverse_family_tokens; + std::vector<base::StringPiece16> reverse_family_tokens; reverse_family_tokens.push_back(name_tokens.back()); name_tokens.pop_back(); while (name_tokens.size() >= 1 && @@ -306,14 +307,14 @@ NameParts SplitName(const base::string16& name) { name_tokens.pop_back(); } - std::vector<base::string16> family_tokens(reverse_family_tokens.rbegin(), - reverse_family_tokens.rend()); + std::vector<base::StringPiece16> family_tokens(reverse_family_tokens.rbegin(), + reverse_family_tokens.rend()); parts.family = base::JoinString(family_tokens, base::ASCIIToUTF16(" ")); // Take the last remaining token as the middle name (if there are at least 2 // tokens). if (name_tokens.size() >= 2) { - parts.middle = name_tokens.back(); + parts.middle = name_tokens.back().as_string(); name_tokens.pop_back(); } @@ -323,11 +324,11 @@ NameParts SplitName(const base::string16& name) { return parts; } -base::string16 JoinNameParts(const base::string16& given, - const base::string16& middle, - const base::string16& family) { +base::string16 JoinNameParts(base::StringPiece16 given, + base::StringPiece16 middle, + base::StringPiece16 family) { // First Middle Last - std::vector<base::string16> full_name; + std::vector<base::StringPiece16> full_name; if (!given.empty()) full_name.push_back(given); @@ -347,7 +348,7 @@ base::string16 JoinNameParts(const base::string16& given, return base::JoinString(full_name, base::ASCIIToUTF16(separator)); } -bool ProfileMatchesFullName(const base::string16 full_name, +bool ProfileMatchesFullName(base::StringPiece16 full_name, const autofill::AutofillProfile& profile) { const base::string16 kSpace = base::ASCIIToUTF16(" "); const base::string16 kPeriodSpace = base::ASCIIToUTF16(". "); diff --git a/chromium/components/autofill/core/browser/autofill_data_util.h b/chromium/components/autofill/core/browser/autofill_data_util.h index d398e476b68..2084c361594 100644 --- a/chromium/components/autofill/core/browser/autofill_data_util.h +++ b/chromium/components/autofill/core/browser/autofill_data_util.h @@ -6,8 +6,8 @@ #define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_DATA_UTIL_H_ #include "base/strings/string16.h" +#include "base/strings/string_piece.h" #include "components/autofill/core/browser/autofill_profile.h" -#include "grit/components_scaled_resources.h" namespace autofill { namespace data_util { @@ -28,23 +28,23 @@ struct PaymentRequestData { // Returns true if |name| looks like a CJK name (or some kind of mish-mash of // the three, at least). -bool IsCJKName(const base::string16& name); +bool IsCJKName(base::StringPiece16 name); // TODO(crbug.com/586510): Investigate the use of app_locale to do better name // splitting. // Returns the different name parts (given, middle and family names) of the full // |name| passed as a parameter. -NameParts SplitName(const base::string16& name); +NameParts SplitName(base::StringPiece16 name); // Concatenates the name parts together in the correct order (based on script), // and returns the result. -base::string16 JoinNameParts(const base::string16& given, - const base::string16& middle, - const base::string16& family); +base::string16 JoinNameParts(base::StringPiece16 given, + base::StringPiece16 middle, + base::StringPiece16 family); // Returns true iff |full_name| is a concatenation of some combination of the // first/middle/last (incl. middle initial) in |profile|. -bool ProfileMatchesFullName(const base::string16 full_name, +bool ProfileMatchesFullName(base::StringPiece16 full_name, const autofill::AutofillProfile& profile); // Returns the Payment Request API basic card payment spec data for the provided diff --git a/chromium/components/autofill/core/browser/autofill_driver.h b/chromium/components/autofill/core/browser/autofill_driver.h index a702eb26a03..e8b95d87a02 100644 --- a/chromium/components/autofill/core/browser/autofill_driver.h +++ b/chromium/components/autofill/core/browser/autofill_driver.h @@ -103,6 +103,10 @@ class AutofillDriver { // Called when the user interacted with a credit card form, so that // the current page's security state can be updated appropriately. virtual void DidInteractWithCreditCardForm() = 0; + + // Tells the associated frame that a user gesture was observed somewhere in + // the tab (including in a different frame). + virtual void NotifyFirstUserGestureObservedInTab() {} }; } // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_driver_factory.cc b/chromium/components/autofill/core/browser/autofill_driver_factory.cc new file mode 100644 index 00000000000..224c1efde47 --- /dev/null +++ b/chromium/components/autofill/core/browser/autofill_driver_factory.cc @@ -0,0 +1,58 @@ +// 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/autofill_driver_factory.h" + +#include "base/callback.h" +#include "components/autofill/core/browser/autofill_client.h" +#include "components/autofill/core/browser/autofill_driver.h" + +namespace autofill { + +AutofillDriverFactory::AutofillDriverFactory(AutofillClient* client) + : client_(client) {} + +AutofillDriverFactory::~AutofillDriverFactory() {} + +AutofillDriver* AutofillDriverFactory::DriverForKey(void* key) { + auto mapping = driver_map_.find(key); + return mapping == driver_map_.end() ? nullptr : mapping->second.get(); +} + +void AutofillDriverFactory::NavigationFinished() { + user_gesture_seen_ = false; + client_->HideAutofillPopup(); +} + +void AutofillDriverFactory::TabHidden() { + client_->HideAutofillPopup(); +} + +void AutofillDriverFactory::OnFirstUserGestureObserved() { + if (user_gesture_seen_) + return; + + for (auto& driver : driver_map_) + driver.second->NotifyFirstUserGestureObservedInTab(); + + user_gesture_seen_ = true; +} + +void AutofillDriverFactory::AddForKey( + void* key, + base::Callback<std::unique_ptr<AutofillDriver>()> factory_method) { + auto insertion_result = driver_map_.insert(std::make_pair(key, nullptr)); + // This can be called twice for the key representing the main frame. + if (insertion_result.second) { + insertion_result.first->second = factory_method.Run(); + if (user_gesture_seen_) + insertion_result.first->second->NotifyFirstUserGestureObservedInTab(); + } +} + +void AutofillDriverFactory::DeleteForKey(void* key) { + driver_map_.erase(key); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_driver_factory.h b/chromium/components/autofill/core/browser/autofill_driver_factory.h new file mode 100644 index 00000000000..ff48bb67ec3 --- /dev/null +++ b/chromium/components/autofill/core/browser/autofill_driver_factory.h @@ -0,0 +1,71 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef AUTOFILL_CORE_BROWSER_AUTOFILL_DRIVER_FACTORY_H_ +#define AUTOFILL_CORE_BROWSER_AUTOFILL_DRIVER_FACTORY_H_ + +#include <memory> +#include <unordered_map> + +#include "base/callback_forward.h" +#include "base/macros.h" + +namespace autofill { + +class AutofillClient; +class AutofillDriver; + +// Manages the lifetime of AutofillDrivers for a particular AutofillClient by +// creating, notifying, retrieving and deleting on demand. +class AutofillDriverFactory { + public: + explicit AutofillDriverFactory(AutofillClient* client); + + ~AutofillDriverFactory(); + + // A convenience function to retrieve an AutofillDriver for the given key or + // null if there is none. + AutofillDriver* DriverForKey(void* key); + + // Handles finished navigation in any of the frames. + void NavigationFinished(); + + // Handles hiding of the corresponding tab. + void TabHidden(); + + // Call this to notify the factory that one of the frames saw a user gesture. + // The factory will distribute this information to all drivers when it comes + // for the first time since the last main frame navigation to a different + // page. It will also notify drivers added later (see AddForKey). + void OnFirstUserGestureObserved(); + + AutofillClient* client() { return client_; }; + + protected: + // The API manipulating the drivers map is protected to guarantee subclasses + // that nothing else can interfere with the map of drivers. + + // Adds a driver, constructed by calling |factory_method|, for |key|. If there + // already is a driver for |key|, |factory_method| is not called. This might + // end up notifying the driver that a user gesture has been observed. + void AddForKey( + void* key, + base::Callback<std::unique_ptr<AutofillDriver>()> factory_method); + + // Deletes the AutofillDriver for |key|. + void DeleteForKey(void* key); + + private: + AutofillClient* const client_; + + std::unordered_map<void*, std::unique_ptr<AutofillDriver>> driver_map_; + + bool user_gesture_seen_ = false; // The state for OnFirstUserGestureObserved. + + DISALLOW_COPY_AND_ASSIGN(AutofillDriverFactory); +}; + +} // namespace autofill + +#endif // AUTOFILL_CORE_BROWSER_AUTOFILL_DRIVER_FACTORY_H_ diff --git a/chromium/components/autofill/core/browser/autofill_driver_factory_unittest.cc b/chromium/components/autofill/core/browser/autofill_driver_factory_unittest.cc new file mode 100644 index 00000000000..8355161333c --- /dev/null +++ b/chromium/components/autofill/core/browser/autofill_driver_factory_unittest.cc @@ -0,0 +1,240 @@ +// 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/autofill_driver_factory.h" + +#include <memory> +#include <utility> + +#include "base/bind_helpers.h" +#include "base/memory/ptr_util.h" +#include "base/message_loop/message_loop.h" +#include "components/autofill/core/browser/test_autofill_client.h" +#include "components/autofill/core/browser/test_autofill_driver.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace autofill { + +namespace { + +class MockAutofillClient : public TestAutofillClient { + public: + MOCK_METHOD0(HideAutofillPopup, void()); +}; + +// Just a stub AutofillDriver implementation which announces its construction +// and desctruction by updating the passed |instance_counter|. It also records +// when "user gesture observed" was signalled to it. +class CountingAutofillDriver : public TestAutofillDriver { + public: + CountingAutofillDriver(int* instance_counter) + : instance_counter_(instance_counter) { + ++*instance_counter; + } + + ~CountingAutofillDriver() override { --*instance_counter_; } + + // Note that EXPECT_CALL cannot be used here, because creation and + // notification of the same driver might be done by a single AddForKey call + // from the test. Therefore tracking the "gesture_observed" flag is done + // explicitly here. + void NotifyFirstUserGestureObservedInTab() override { + gesture_observed_ = true; + } + + bool gesture_observed() { return gesture_observed_; } + + private: + int* const instance_counter_; + bool gesture_observed_ = false; + + DISALLOW_COPY_AND_ASSIGN(CountingAutofillDriver); +}; + +// Code-wise this class is identitcal to AutofillDriverFactory, but exposes the +// protected API to the test. Do not modify any of the methods, only include +// "using" declarations to make the AutofillDriverFactory methods public. +class PublicAutofillDriverFactory : public AutofillDriverFactory { + public: + explicit PublicAutofillDriverFactory(AutofillClient* client) + : AutofillDriverFactory(client) {} + + ~PublicAutofillDriverFactory() {} + + using AutofillDriverFactory::AddForKey; + using AutofillDriverFactory::DeleteForKey; +}; + +// Wrapper around an integer, checking that the integer is 0 on desctruction. +class CheckedInt { + public: + CheckedInt() {} + + ~CheckedInt() { EXPECT_EQ(0, val_); } + + int* val() { return &val_; } + + private: + int val_ = 0; +}; + +} // namespace + +class AutofillDriverFactoryTest : public testing::Test { + public: + AutofillDriverFactoryTest() : factory_(&client_) {} + + ~AutofillDriverFactoryTest() override {} + + // AutofillDriverFactory stores drivers in a map with keys, which are void* + // pointers. The factory never dereferences them, so their value does not + // matter. This is a handy function to create such pointers from integer + // constants. + void* KeyFrom(int x) { return reinterpret_cast<void*>(x); } + + // Convenience accessor with a cast to CountingAutofillDriver. + CountingAutofillDriver* GetDriver(void* key) { + return static_cast<CountingAutofillDriver*>(factory_.DriverForKey(key)); + } + + std::unique_ptr<AutofillDriver> CreateDriver() { + ++drivers_created_; + return base::MakeUnique<CountingAutofillDriver>(instance_counter_.val()); + } + + base::Callback<std::unique_ptr<AutofillDriver>()> CreateDriverCallback() { + return base::Bind(&AutofillDriverFactoryTest::CreateDriver, + base::Unretained(this)); + } + + protected: + base::MessageLoop message_loop_; // For TestAutofillDriver. + + MockAutofillClient client_; + + CheckedInt instance_counter_; + + PublicAutofillDriverFactory factory_; + + // How many AutofillDriver instances were created with CreateDriver(). + int drivers_created_ = 0; +}; + +TEST_F(AutofillDriverFactoryTest, DriverForKey_NoKey) { + EXPECT_FALSE(factory_.DriverForKey(nullptr)); + EXPECT_FALSE(factory_.DriverForKey(KeyFrom(1))); +} + +TEST_F(AutofillDriverFactoryTest, DriverForKey_OneKey) { + factory_.AddForKey(KeyFrom(1), CreateDriverCallback()); + EXPECT_FALSE(factory_.DriverForKey(nullptr)); + EXPECT_TRUE(factory_.DriverForKey(KeyFrom(1))); +} + +TEST_F(AutofillDriverFactoryTest, DriverForKey_TwoKeys) { + factory_.AddForKey(KeyFrom(1), CreateDriverCallback()); + EXPECT_FALSE(factory_.DriverForKey(nullptr)); + EXPECT_TRUE(factory_.DriverForKey(KeyFrom(1))); + EXPECT_EQ(1, *instance_counter_.val()); + + factory_.AddForKey(nullptr, CreateDriverCallback()); + EXPECT_TRUE(factory_.DriverForKey(nullptr)); + EXPECT_TRUE(factory_.DriverForKey(KeyFrom(1))); + EXPECT_EQ(2, *instance_counter_.val()); +} + +TEST_F(AutofillDriverFactoryTest, AddForKey_Duplicated) { + EXPECT_FALSE(factory_.DriverForKey(KeyFrom(1))); + + factory_.AddForKey(KeyFrom(1), CreateDriverCallback()); + EXPECT_TRUE(factory_.DriverForKey(KeyFrom(1))); + EXPECT_EQ(1, drivers_created_); + EXPECT_EQ(1, *instance_counter_.val()); + + factory_.AddForKey(KeyFrom(1), CreateDriverCallback()); + EXPECT_TRUE(factory_.DriverForKey(KeyFrom(1))); + EXPECT_EQ(1, drivers_created_); + EXPECT_EQ(1, *instance_counter_.val()); +} + +TEST_F(AutofillDriverFactoryTest, DeleteForKey) { + EXPECT_FALSE(factory_.DriverForKey(KeyFrom(1))); + EXPECT_EQ(0, *instance_counter_.val()); + + factory_.AddForKey(KeyFrom(1), CreateDriverCallback()); + EXPECT_TRUE(factory_.DriverForKey(KeyFrom(1))); + EXPECT_EQ(1, *instance_counter_.val()); + + factory_.DeleteForKey(KeyFrom(1)); + EXPECT_FALSE(factory_.DriverForKey(KeyFrom(1))); + EXPECT_EQ(0, *instance_counter_.val()); + + // Duplicated calls should raise no errors. + factory_.DeleteForKey(KeyFrom(1)); + EXPECT_FALSE(factory_.DriverForKey(KeyFrom(1))); + EXPECT_EQ(0, *instance_counter_.val()); +} + +TEST_F(AutofillDriverFactoryTest, NavigationFinished) { + EXPECT_CALL(client_, HideAutofillPopup()); + factory_.NavigationFinished(); +} + +TEST_F(AutofillDriverFactoryTest, TabHidden) { + EXPECT_CALL(client_, HideAutofillPopup()); + factory_.TabHidden(); +} + +// Without calling OnFirstUserGestureObserved on the factory, the factory will +// not call NotifyFirstUserGestureObservedInTab on a driver. +TEST_F(AutofillDriverFactoryTest, OnFirstUserGestureObserved_NotCalled) { + factory_.AddForKey(KeyFrom(1), CreateDriverCallback()); + EXPECT_FALSE(GetDriver(KeyFrom(1))->gesture_observed()); +} + +// Call OnFirstUserGestureObserved on the factory with one driver. The factory +// will call NotifyFirstUserGestureObservedInTab on that driver. +TEST_F(AutofillDriverFactoryTest, OnFirstUserGestureObserved_CalledOld) { + factory_.AddForKey(KeyFrom(1), CreateDriverCallback()); + factory_.OnFirstUserGestureObserved(); + EXPECT_TRUE(GetDriver(KeyFrom(1))->gesture_observed()); +} + +// Call OnFirstUserGestureObserved on the factory without drivers. Add a +// driver. The factory will call NotifyFirstUserGestureObservedInTab on that +// driver. +TEST_F(AutofillDriverFactoryTest, OnFirstUserGestureObserved_CalledNew) { + factory_.OnFirstUserGestureObserved(); + factory_.AddForKey(KeyFrom(1), CreateDriverCallback()); + EXPECT_TRUE(GetDriver(KeyFrom(1))->gesture_observed()); +} + +// Combining the CalledOld and CalledNew test cases into one. +TEST_F(AutofillDriverFactoryTest, OnFirstUserGestureObserved_MultipleDrivers) { + factory_.AddForKey(KeyFrom(1), CreateDriverCallback()); + factory_.OnFirstUserGestureObserved(); + EXPECT_TRUE(GetDriver(KeyFrom(1))->gesture_observed()); + + factory_.AddForKey(KeyFrom(7), CreateDriverCallback()); + EXPECT_TRUE(GetDriver(KeyFrom(7))->gesture_observed()); +} + +// Call OnFirstUserGestureObserved on the factory with one driver. Simulate +// navigation to a different page. Add a driver. The factory will not call +// NotifyFirstUserGestureObservedInTab on that driver. +TEST_F(AutofillDriverFactoryTest, OnFirstUserGestureObserved_CalledNavigation) { + factory_.AddForKey(KeyFrom(1), CreateDriverCallback()); + factory_.OnFirstUserGestureObserved(); + EXPECT_TRUE(GetDriver(KeyFrom(1))->gesture_observed()); + + EXPECT_CALL(client_, HideAutofillPopup()); + factory_.NavigationFinished(); + + // Adding a sub-frame + factory_.AddForKey(KeyFrom(2), CreateDriverCallback()); + EXPECT_FALSE(GetDriver(KeyFrom(2))->gesture_observed()); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_experiments.cc b/chromium/components/autofill/core/browser/autofill_experiments.cc index 39472262445..b27569f4d8c 100644 --- a/chromium/components/autofill/core/browser/autofill_experiments.cc +++ b/chromium/components/autofill/core/browser/autofill_experiments.cc @@ -16,33 +16,36 @@ #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/strings/grit/components_strings.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 { const base::Feature kAutofillCreditCardAssist{ "AutofillCreditCardAssist", base::FEATURE_DISABLED_BY_DEFAULT}; -const base::Feature kAutofillCreditCardSigninPromo{ - "AutofillCreditCardSigninPromo", 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 base::Feature kAutofillCreditCardLastUsedDateDisplay{ + "AutofillCreditCardLastUsedDateDisplay", base::FEATURE_DISABLED_BY_DEFAULT}; +const base::Feature kAutofillUkmLogging{"kAutofillUkmLogging", + 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 kAutofillPopupDropdownItemHeightKey[] = "dropdown_item_height"; const char kAutofillCreditCardPopupIsIconAtStartKey[] = "is_credit_card_icon_at_start"; const char kAutofillPopupMarginKey[] = "margin"; +const char kAutofillCreditCardLastUsedDateShowExpirationDateKey[] = + "show_expiration_date"; namespace { @@ -70,10 +73,6 @@ bool IsInAutofillSuggestionsDisabledExperiment() { return group_name == "Disabled"; } -bool IsAutofillCreditCardSigninPromoEnabled() { - return base::FeatureList::IsEnabled(kAutofillCreditCardSigninPromo); -} - bool IsAutofillCreditCardAssistEnabled() { #if !defined(OS_ANDROID) && !defined(OS_IOS) return false; @@ -82,21 +81,14 @@ bool IsAutofillCreditCardAssistEnabled() { #endif } -int GetCreditCardSigninPromoImpressionLimit() { - int impression_limit; - std::string param_value = variations::GetVariationParamValueByFeature( - kAutofillCreditCardSigninPromo, - kCreditCardSigninPromoImpressionLimitParamKey); - if (!param_value.empty() && base::StringToInt(param_value, &impression_limit)) - return impression_limit; - - return 0; -} - bool IsAutofillCreditCardPopupLayoutExperimentEnabled() { return base::FeatureList::IsEnabled(kAutofillCreditCardPopupLayout); } +bool IsAutofillCreditCardLastUsedDateDisplayExperimentEnabled() { + return base::FeatureList::IsEnabled(kAutofillCreditCardLastUsedDateDisplay); +} + // |GetCreditCardPopupParameterUintValue| returns 0 if experiment parameter is // not specified. 0 == |SK_ColorTRANSPARENT|. SkColor GetCreditCardPopupBackgroundColor() { @@ -126,6 +118,13 @@ bool IsIconInCreditCardPopupAtStart() { return param_value == "true"; } +bool ShowExpirationDateInAutofillCreditCardLastUsedDate() { + const std::string param_value = variations::GetVariationParamValueByFeature( + kAutofillCreditCardLastUsedDateDisplay, + kAutofillCreditCardLastUsedDateShowExpirationDateKey); + 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). @@ -226,4 +225,8 @@ bool IsCreditCardUploadEnabled(const PrefService* pref_service, return !group_name.empty() && group_name != "Disabled"; } +bool IsUkmLoggingEnabled() { + return base::FeatureList::IsEnabled(kAutofillUkmLogging); +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_experiments.h b/chromium/components/autofill/core/browser/autofill_experiments.h index 4467e38d9b3..70c3f411c60 100644 --- a/chromium/components/autofill/core/browser/autofill_experiments.h +++ b/chromium/components/autofill/core/browser/autofill_experiments.h @@ -25,11 +25,13 @@ namespace autofill { struct Suggestion; extern const base::Feature kAutofillCreditCardAssist; -extern const base::Feature kAutofillCreditCardSigninPromo; extern const base::Feature kAutofillScanCardholderName; extern const base::Feature kAutofillCreditCardPopupLayout; +extern const base::Feature kAutofillCreditCardLastUsedDateDisplay; +extern const base::Feature kAutofillUkmLogging; extern const char kCreditCardSigninPromoImpressionLimitParamKey[]; extern const char kAutofillCreditCardPopupSettingsSuggestionValueKey[]; +extern const char kAutofillCreditCardLastUsedDateShowExpirationDateKey[]; // Returns true if autofill should be enabled. See also // IsInAutofillSuggestionsDisabledExperiment below. @@ -41,16 +43,9 @@ bool IsAutofillEnabled(const PrefService* pref_service); // disables providing suggestions. bool IsInAutofillSuggestionsDisabledExperiment(); -// Returns whether the Autofill credit card signin promo should be shown. -bool IsAutofillCreditCardSigninPromoEnabled(); - // Returns whether the Autofill credit card assist infobar should be shown. bool IsAutofillCreditCardAssistEnabled(); -// Returns the maximum number of impressions of the credit card signin promo, or -// 0 if there are no limits. -int GetCreditCardSigninPromoImpressionLimit(); - // Returns true if the user should be offered to locally store unmasked cards. // This controls whether the option is presented at all rather than the default // response of the option. @@ -67,6 +62,13 @@ bool IsCreditCardUploadEnabled(const PrefService* pref_service, // enabled. bool IsAutofillCreditCardPopupLayoutExperimentEnabled(); +// Returns whether Autofill credit card last used date display experiment is +// enabled. +bool IsAutofillCreditCardLastUsedDateDisplayExperimentEnabled(); + +// Returns whether Autofill credit card last used date shows expiration date. +bool ShowExpirationDateInAutofillCreditCardLastUsedDate(); + // Returns the background color for credit card autofill popup, or // |SK_ColorTRANSPARENT| if the new credit card autofill popup layout experiment // is not enabled. @@ -99,6 +101,9 @@ void ModifyAutofillCreditCardSuggestion(struct Suggestion* suggestion); // layout. unsigned int GetPopupMargin(); +// Returns whether the feature to log UKMs is enabled. +bool IsUkmLoggingEnabled(); + } // namespace autofill #endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_EXPERIMENTS_H_ diff --git a/chromium/components/autofill/core/browser/autofill_external_delegate.cc b/chromium/components/autofill/core/browser/autofill_external_delegate.cc index e80c8ac5461..5853a8de1d7 100644 --- a/chromium/components/autofill/core/browser/autofill_external_delegate.cc +++ b/chromium/components/autofill/core/browser/autofill_external_delegate.cc @@ -24,7 +24,7 @@ #include "components/autofill/core/browser/autofill_metrics.h" #include "components/autofill/core/browser/popup_item_ids.h" #include "components/autofill/core/common/autofill_util.h" -#include "grit/components_strings.h" +#include "components/strings/grit/components_strings.h" #include "ui/base/l10n/l10n_util.h" namespace autofill { diff --git a/chromium/components/autofill/core/browser/autofill_field.cc b/chromium/components/autofill/core/browser/autofill_field.cc index bf57caec2f6..3704b10fd12 100644 --- a/chromium/components/autofill/core/browser/autofill_field.cc +++ b/chromium/components/autofill/core/browser/autofill_field.cc @@ -25,7 +25,7 @@ #include "components/autofill/core/common/autofill_l10n_util.h" #include "components/autofill/core/common/autofill_switches.h" #include "components/autofill/core/common/autofill_util.h" -#include "grit/components_strings.h" +#include "components/strings/grit/components_strings.h" #include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_data.h" #include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_formatter.h" #include "ui/base/l10n/l10n_util.h" diff --git a/chromium/components/autofill/core/browser/autofill_manager.cc b/chromium/components/autofill/core/browser/autofill_manager.cc index 977e67ae874..056793fe09b 100644 --- a/chromium/components/autofill/core/browser/autofill_manager.cc +++ b/chromium/components/autofill/core/browser/autofill_manager.cc @@ -51,6 +51,7 @@ #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_clock.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" @@ -64,8 +65,8 @@ #include "components/rappor/public/rappor_utils.h" #include "components/rappor/rappor_service_impl.h" #include "components/security_state/core/security_state.h" +#include "components/strings/grit/components_strings.h" #include "google_apis/gaia/identity_provider.h" -#include "grit/components_strings.h" #include "ui/base/l10n/l10n_util.h" #include "ui/gfx/geometry/rect.h" #include "url/gurl.h" @@ -79,6 +80,8 @@ namespace autofill { using base::StartsWith; using base::TimeTicks; +const int kCreditCardSigninPromoImpressionLimit = 3; + namespace { const size_t kMaxRecentFormSignaturesToRemember = 3; @@ -306,6 +309,9 @@ bool AutofillManager::ShouldShowScanCreditCard(const FormData& form, if (!is_card_number_field && !is_scannable_name_on_card_field) return false; + if (IsFormNonSecure(form)) + return false; + static const int kShowScanCreditCardMaxValueLength = 6; return field.value.size() <= kShowScanCreditCardMaxValueLength; } @@ -319,9 +325,6 @@ bool AutofillManager::IsCreditCardPopup(const FormData& form, bool AutofillManager::ShouldShowCreditCardSigninPromo( const FormData& form, const FormFieldData& field) { - if (!IsAutofillCreditCardSigninPromoEnabled()) - return false; - // Check whether we are dealing with a credit card field and whether it's // appropriate to show the promo (e.g. the platform is supported). AutofillField* autofill_field = GetAutofillField(form, field); @@ -329,12 +332,13 @@ bool AutofillManager::ShouldShowCreditCardSigninPromo( !client_->ShouldShowSigninPromo()) return false; - // The last step is checking if we are under the limit of impressions (a limit - // of 0 means there is no limit); - int impression_limit = GetCreditCardSigninPromoImpressionLimit(); + if (IsFormNonSecure(form)) + return false; + + // The last step is checking if we are under the limit of impressions. int impression_count = client_->GetPrefs()->GetInteger( prefs::kAutofillCreditCardSigninPromoImpressionCount); - if (impression_limit == 0 || impression_count < impression_limit) { + if (impression_count < kCreditCardSigninPromoImpressionLimit) { // The promo will be shown. Increment the impression count. client_->GetPrefs()->SetInteger( prefs::kAutofillCreditCardSigninPromoImpressionCount, @@ -529,6 +533,11 @@ void AutofillManager::OnTextFieldDidChange(const FormData& form, UpdateInitialInteractionTimestamp(timestamp); } +bool AutofillManager::IsFormNonSecure(const FormData& form) const { + return !client_->IsContextSecure() || + (form.action.is_valid() && form.action.SchemeIs("http")); +} + void AutofillManager::OnQueryFormFieldAutofill(int query_id, const FormData& form, const FormFieldData& field, @@ -538,6 +547,7 @@ void AutofillManager::OnQueryFormFieldAutofill(int query_id, gfx::RectF transformed_box = driver_->TransformBoundingBoxToViewportCoordinates(bounding_box); + external_delegate_->OnQuery(query_id, form, field, transformed_box); // Need to refresh models before using the form_event_loggers. @@ -564,14 +574,14 @@ 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_context_secure = !IsFormNonSecure(form); const bool is_http_warning_enabled = security_state::IsHttpWarningInFormEnabled(); + // TODO(rogerm): Early exit here on !driver_->RendererIsAvailable()? + // We skip populating autofill data, but might generate warnings and or + // signin promo to show over the unavailable renderer. That seems a mistake. + if (is_autofill_possible && driver_->RendererIsAvailable() && got_autofillable_form) { @@ -723,7 +733,7 @@ void AutofillManager::FillOrPreviewCreditCardForm( masked_card_ = credit_card; GetOrCreateFullCardRequest()->GetFullCard( masked_card_, AutofillClient::UNMASK_FOR_AUTOFILL, - weak_ptr_factory_.GetWeakPtr()); + weak_ptr_factory_.GetWeakPtr(), weak_ptr_factory_.GetWeakPtr()); credit_card_form_event_logger_->OnDidSelectMaskedServerCardSuggestion(); return; } @@ -1029,6 +1039,7 @@ void AutofillManager::OnDidGetUploadDetails( weak_ptr_factory_.GetWeakPtr())); AutofillMetrics::LogCardUploadDecisionMetric( AutofillMetrics::UPLOAD_OFFERED); + LogCardUploadDecisionUkm(AutofillMetrics::UPLOAD_OFFERED); } else { // If the upload details request failed, fall back to a local save. The // reasoning here is as follows: @@ -1048,7 +1059,10 @@ void AutofillManager::OnDidGetUploadDetails( base::Unretained(personal_data_), upload_request_.card)); AutofillMetrics::LogCardUploadDecisionMetric( AutofillMetrics::UPLOAD_NOT_OFFERED_GET_UPLOAD_DETAILS_FAILED); + LogCardUploadDecisionUkm( + AutofillMetrics::UPLOAD_NOT_OFFERED_GET_UPLOAD_DETAILS_FAILED); } + pending_upload_request_url_ = GURL(); } void AutofillManager::OnDidUploadCard( @@ -1069,6 +1083,18 @@ void AutofillManager::OnFullCardRequestFailed() { driver_->RendererShouldClearPreviewedForm(); } +void AutofillManager::ShowUnmaskPrompt( + const CreditCard& card, + AutofillClient::UnmaskCardReason reason, + base::WeakPtr<CardUnmaskDelegate> delegate) { + client_->ShowUnmaskPrompt(card, reason, delegate); +} + +void AutofillManager::OnUnmaskVerificationResult( + AutofillClient::PaymentsRpcResult result) { + client_->OnUnmaskVerificationResult(result); +} + void AutofillManager::OnUserDidAcceptUpload() { user_did_accept_upload_prompt_ = true; if (!upload_request_.risk_data.empty()) { @@ -1165,6 +1191,13 @@ void AutofillManager::ImportFormData(const FormStructure& submitted_form) { upload_request_ = payments::PaymentsClient::UploadRequestDetails(); upload_request_.card = *imported_credit_card; + // In order to prompt the user to upload their card, we must have both: + // 1) Card with CVC + // 2) 1+ recently-used or modified addresses that meet the client-side + // validation rules + // Here we perform both checks before returning or logging anything, + // because if only one of the two is missing, it may be fixable. + // Check for a CVC to determine whether we can prompt the user to upload // 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 @@ -1177,19 +1210,40 @@ void AutofillManager::ImportFormData(const FormStructure& submitted_form) { break; } } + + // Upload requires that recently used or modified addresses meet the + // client-side validation rules. + autofill::AutofillMetrics::CardUploadDecisionMetric + get_profiles_decision_metric = AutofillMetrics::UPLOAD_OFFERED; + std::string rappor_metric_name; + bool get_profiles_succeeded = + GetProfilesForCreditCardUpload(*imported_credit_card, + &upload_request_.profiles, + &get_profiles_decision_metric, + &rappor_metric_name); + + pending_upload_request_url_ = GURL(submitted_form.source_url()); + + // Both the CVC and address checks are done. Conform to the legacy order of + // reporting on CVC then address. if (upload_request_.cvc.empty()) { AutofillMetrics::LogCardUploadDecisionMetric( AutofillMetrics::UPLOAD_NOT_OFFERED_NO_CVC); + LogCardUploadDecisionUkm(AutofillMetrics::UPLOAD_NOT_OFFERED_NO_CVC); + pending_upload_request_url_ = GURL(); CollectRapportSample(submitted_form.source_url(), "Autofill.CardUploadNotOfferedNoCvc"); return; } - - // Upload also requires recently used or modified addresses that meet the - // client-side validation rules. - if (!GetProfilesForCreditCardUpload(*imported_credit_card, - &upload_request_.profiles, - submitted_form.source_url())) { + if (!get_profiles_succeeded) { + DCHECK(get_profiles_decision_metric != AutofillMetrics::UPLOAD_OFFERED); + AutofillMetrics::LogCardUploadDecisionMetric( + get_profiles_decision_metric); + LogCardUploadDecisionUkm(get_profiles_decision_metric); + pending_upload_request_url_ = GURL(); + if (!rappor_metric_name.empty()) { + CollectRapportSample(submitted_form.source_url(), rappor_metric_name); + } return; } @@ -1201,9 +1255,11 @@ void AutofillManager::ImportFormData(const FormStructure& submitted_form) { bool AutofillManager::GetProfilesForCreditCardUpload( const CreditCard& card, std::vector<AutofillProfile>* profiles, - const GURL& source_url) const { + autofill::AutofillMetrics::CardUploadDecisionMetric* + address_upload_decision_metric, + std::string* rappor_metric_name) const { std::vector<AutofillProfile> candidate_profiles; - const base::Time now = base::Time::Now(); + const base::Time now = AutofillClock::Now(); const base::TimeDelta fifteen_minutes = base::TimeDelta::FromMinutes(15); // First, collect all of the addresses used recently. @@ -1214,9 +1270,9 @@ bool AutofillManager::GetProfilesForCreditCardUpload( } } if (candidate_profiles.empty()) { - AutofillMetrics::LogCardUploadDecisionMetric( - AutofillMetrics::UPLOAD_NOT_OFFERED_NO_ADDRESS); - CollectRapportSample(source_url, "Autofill.CardUploadNotOfferedNoAddress"); + *address_upload_decision_metric = + AutofillMetrics::UPLOAD_NOT_OFFERED_NO_ADDRESS; + *rappor_metric_name = "Autofill.CardUploadNotOfferedNoAddress"; return false; } @@ -1244,10 +1300,9 @@ bool AutofillManager::GetProfilesForCreditCardUpload( // countries, we'll need to make the name comparison more sophisticated. if (!base::EqualsCaseInsensitiveASCII( verified_name, RemoveMiddleInitial(address_name))) { - AutofillMetrics::LogCardUploadDecisionMetric( - AutofillMetrics::UPLOAD_NOT_OFFERED_CONFLICTING_NAMES); - CollectRapportSample(source_url, - "Autofill.CardUploadNotOfferedConflictingNames"); + *address_upload_decision_metric = + AutofillMetrics::UPLOAD_NOT_OFFERED_CONFLICTING_NAMES; + *rappor_metric_name = "Autofill.CardUploadNotOfferedConflictingNames"; return false; } } @@ -1256,9 +1311,9 @@ bool AutofillManager::GetProfilesForCreditCardUpload( // If neither the card nor any of the addresses have a name associated with // them, the candidate set is invalid. if (verified_name.empty()) { - AutofillMetrics::LogCardUploadDecisionMetric( - AutofillMetrics::UPLOAD_NOT_OFFERED_NO_NAME); - CollectRapportSample(source_url, "Autofill.CardUploadNotOfferedNoName"); + *address_upload_decision_metric = + AutofillMetrics::UPLOAD_NOT_OFFERED_NO_NAME; + *rappor_metric_name = "Autofill.CardUploadNotOfferedNoName"; return false; } @@ -1284,8 +1339,8 @@ bool AutofillManager::GetProfilesForCreditCardUpload( // likely to fail. if (!(StartsWith(verified_zip, zip, base::CompareCase::SENSITIVE) || StartsWith(zip, verified_zip, base::CompareCase::SENSITIVE))) { - AutofillMetrics::LogCardUploadDecisionMetric( - AutofillMetrics::UPLOAD_NOT_OFFERED_CONFLICTING_ZIPS); + *address_upload_decision_metric = + AutofillMetrics::UPLOAD_NOT_OFFERED_CONFLICTING_ZIPS; return false; } } @@ -1295,8 +1350,8 @@ bool AutofillManager::GetProfilesForCreditCardUpload( // If none of the candidate addresses have a zip, the candidate set is // invalid. if (verified_zip.empty()) { - AutofillMetrics::LogCardUploadDecisionMetric( - AutofillMetrics::UPLOAD_NOT_OFFERED_NO_ZIP_CODE); + *address_upload_decision_metric = + AutofillMetrics::UPLOAD_NOT_OFFERED_NO_ZIP_CODE; return false; } @@ -1305,7 +1360,8 @@ bool AutofillManager::GetProfilesForCreditCardUpload( } void AutofillManager::CollectRapportSample(const GURL& source_url, - const char* metric_name) const { + const std::string& metric_name) + const { if (source_url.is_valid() && client_->GetRapporServiceImpl()) { rappor::SampleDomainAndRegistryFromGURL(client_->GetRapporServiceImpl(), metric_name, source_url); @@ -1573,7 +1629,7 @@ void AutofillManager::FillOrPreviewDataModelForm( } else if (is_credit_card && IsCreditCardExpirationType( cached_field->Type().GetStorableType()) && static_cast<const CreditCard*>(&data_model) - ->IsExpired(base::Time::Now())) { + ->IsExpired(AutofillClock::Now())) { // Don't fill expired cards expiration date. value = base::string16(); } @@ -1871,7 +1927,7 @@ void AutofillManager::ParseForms(const std::vector<FormData>& forms) { personal_data_->GetCreditCardsToSuggest(); // Expired cards are last in the sorted order, so if the first one is // expired, they all are. - if (!cards.empty() && !cards.front()->IsExpired(base::Time::Now())) + if (!cards.empty() && !cards.front()->IsExpired(AutofillClock::Now())) autofill_assistant_.ShowAssistForCreditCard(*cards.front()); } #endif @@ -2151,4 +2207,10 @@ void AutofillManager::DumpAutofillData(bool imported_cc) const { } #endif // ENABLE_FORM_DEBUG_DUMP +void AutofillManager::LogCardUploadDecisionUkm( + AutofillMetrics::CardUploadDecisionMetric upload_decision) { + AutofillMetrics::LogCardUploadDecisionUkm( + client_->GetUkmService(), pending_upload_request_url_, upload_decision); +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_manager.h b/chromium/components/autofill/core/browser/autofill_manager.h index ce8608f03db..290126ec108 100644 --- a/chromium/components/autofill/core/browser/autofill_manager.h +++ b/chromium/components/autofill/core/browser/autofill_manager.h @@ -43,6 +43,8 @@ #define ENABLE_FORM_DEBUG_DUMP #endif +class GURL; + namespace gfx { class RectF; } @@ -67,11 +69,15 @@ class FormStructureBrowserTest; struct FormData; struct FormFieldData; +// We show the credit card signin promo only a certain number of times. +extern const int kCreditCardSigninPromoImpressionLimit; + // Manages saving and restoring the user's personal information entered into web // forms. One per frame; owned by the AutofillDriver. class AutofillManager : public AutofillDownloadManager::Observer, public payments::PaymentsClientDelegate, - public payments::FullCardRequest::Delegate { + public payments::FullCardRequest::ResultDelegate, + public payments::FullCardRequest::UIDelegate { public: enum AutofillDownloadManagerState { ENABLE_AUTOFILL_DOWNLOAD_MANAGER, @@ -153,6 +159,11 @@ class AutofillManager : public AutofillDownloadManager::Observer, payments::FullCardRequest* GetOrCreateFullCardRequest(); + base::WeakPtr<payments::FullCardRequest::UIDelegate> + GetAsFullCardRequestUIDelegate() { + return weak_ptr_factory_.GetWeakPtr(); + } + const std::string& app_locale() const { return app_locale_; } // Only for testing. @@ -290,11 +301,18 @@ class AutofillManager : public AutofillDownloadManager::Observer, std::unique_ptr<base::DictionaryValue> legal_message) override; void OnDidUploadCard(AutofillClient::PaymentsRpcResult result) override; - // FullCardRequest::Delegate: + // payments::FullCardRequest::ResultDelegate: void OnFullCardRequestSucceeded(const CreditCard& card, const base::string16& cvc) override; void OnFullCardRequestFailed() override; + // payments::FullCardRequest::UIDelegate: + void ShowUnmaskPrompt(const CreditCard& card, + AutofillClient::UnmaskCardReason reason, + base::WeakPtr<CardUnmaskDelegate> delegate) override; + void OnUnmaskVerificationResult( + AutofillClient::PaymentsRpcResult result) override; + // Sets |user_did_accept_upload_prompt_| and calls UploadCard if the risk data // is available. void OnUserDidAcceptUpload(); @@ -401,15 +419,19 @@ class AutofillManager : public AutofillDownloadManager::Observer, // Logs |metric_name| with RAPPOR, for the specific form |source_url|. void CollectRapportSample(const GURL& source_url, - const char* metric_name) const; + const std::string& metric_name) const; // Examines |card| and the stored profiles and if a candidate set of profiles // is found that matches the client-side validation rules, assigns the values - // to |profiles|. |source_url| is the source URL for the form. If no valid set - // can be found, returns false. + // to |profiles|. If no valid set can be found, returns false, assigns the + // failure reason to |address_upload_decision_metric|, and if applicable, the + // RAPPOR metric to log to |rappor_metric_name|. bool GetProfilesForCreditCardUpload(const CreditCard& card, std::vector<AutofillProfile>* profiles, - const GURL& source_url) const; + autofill::AutofillMetrics:: + CardUploadDecisionMetric* + address_upload_decision_metric, + std::string* rappor_metric_name) const; // If |initial_interaction_timestamp_| is unset or is set to a later time than // |interaction_timestamp|, updates the cached timestamp. The latter check is @@ -417,6 +439,10 @@ class AutofillManager : public AutofillDownloadManager::Observer, void UpdateInitialInteractionTimestamp( const base::TimeTicks& interaction_timestamp); + // Examines |form| and returns true if it is in a non-secure context or + // its action attribute targets a HTTP url. + bool IsFormNonSecure(const FormData& form) const; + // Uses the existing personal data in |profiles| and |credit_cards| to // determine possible field types for the |submitted_form|. This is // potentially expensive -- on the order of 50ms even for a small set of @@ -451,6 +477,10 @@ class AutofillManager : public AutofillDownloadManager::Observer, void DumpAutofillData(bool imported_cc) const; #endif + // Logs the card upload decision UKM. + void LogCardUploadDecisionUkm( + AutofillMetrics::CardUploadDecisionMetric upload_decision); + // Provides driver-level context to the shared code of the component. Must // outlive this object. AutofillDriver* driver_; @@ -521,6 +551,7 @@ class AutofillManager : public AutofillDownloadManager::Observer, // Collected information about a pending upload request. payments::PaymentsClient::UploadRequestDetails upload_request_; bool user_did_accept_upload_prompt_; + GURL pending_upload_request_url_; #ifdef ENABLE_FORM_DEBUG_DUMP // The last few autofilled forms (key/value pairs) submitted, for debugging. diff --git a/chromium/components/autofill/core/browser/autofill_manager_unittest.cc b/chromium/components/autofill/core/browser/autofill_manager_unittest.cc index 38264eda18c..54643c65a6a 100644 --- a/chromium/components/autofill/core/browser/autofill_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_manager_unittest.cc @@ -18,6 +18,7 @@ #include "base/memory/ref_counted.h" #include "base/memory/scoped_vector.h" #include "base/metrics/field_trial.h" +#include "base/metrics/metrics_hashes.h" #include "base/run_loop.h" #include "base/strings/string16.h" #include "base/strings/string_number_conversions.h" @@ -47,11 +48,15 @@ #include "components/autofill/core/common/autofill_util.h" #include "components/autofill/core/common/form_data.h" #include "components/autofill/core/common/form_field_data.h" +#include "components/metrics/proto/ukm/entry.pb.h" #include "components/prefs/pref_service.h" #include "components/rappor/test_rappor_service.h" #include "components/security_state/core/security_state.h" +#include "components/strings/grit/components_strings.h" +#include "components/ukm/test_ukm_service.h" +#include "components/ukm/ukm_entry.h" +#include "components/ukm/ukm_source.h" #include "components/variations/variations_associated_data.h" -#include "grit/components_strings.h" #include "net/url_request/url_request_test_util.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -774,6 +779,17 @@ class TestAutofillExternalDelegate : public AutofillExternalDelegate { DISALLOW_COPY_AND_ASSIGN(TestAutofillExternalDelegate); }; +// Finds the specified UKM metric by |name| in the specified UKM |metrics|. +const ukm::Entry_Metric* FindMetric( + const char* name, + const google::protobuf::RepeatedPtrField<ukm::Entry_Metric>& metrics) { + for (const auto& metric : metrics) { + if (metric.metric_hash() == base::HashMetricName(name)) + return &metric; + } + return nullptr; +} + } // namespace class AutofillManagerTest : public testing::Test { @@ -817,32 +833,6 @@ class AutofillManagerTest : public testing::Test { request_context_ = nullptr; } - void EnableCreditCardSigninPromoFeatureWithLimit(int impression_limit) { - const std::string kTrialName = "MyTrial"; - const std::string kGroupName = "Group1"; - - scoped_refptr<base::FieldTrial> trial( - base::FieldTrialList::CreateFieldTrial(kTrialName, kGroupName)); - std::map<std::string, std::string> params; - params[kCreditCardSigninPromoImpressionLimitParamKey] = - base::IntToString(impression_limit); - ASSERT_TRUE( - variations::AssociateVariationParams(kTrialName, kGroupName, params)); - - // Enable the feature. - std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList); - feature_list->RegisterFieldTrialOverride( - kAutofillCreditCardSigninPromo.name, - base::FeatureList::OVERRIDE_ENABLE_FEATURE, trial.get()); - scoped_feature_list_.InitWithFeatureList(std::move(feature_list)); - - // Double-checking our params made it. - std::map<std::string, std::string> actualParams; - EXPECT_TRUE(variations::GetVariationParamsByFeature( - kAutofillCreditCardSigninPromo, &actualParams)); - EXPECT_EQ(params, actualParams); - } - void GetAutofillSuggestions(int query_id, const FormData& form, const FormFieldData& field) { @@ -1024,6 +1014,39 @@ class AutofillManagerTest : public testing::Test { security_state::kHttpFormWarningFeature); } + void EnableUkmLogging() { + scoped_feature_list_.InitAndEnableFeature(kAutofillUkmLogging); + } + + void ExpectUniqueCardUploadDecisionUkm( + AutofillMetrics::CardUploadDecisionMetric upload_decision) { + ukm::TestUkmService* ukm_service = autofill_client_.GetTestUkmService(); + + // Check that one source is logged. + ASSERT_EQ(1U, ukm_service->sources_count()); + const ukm::UkmSource* source = ukm_service->GetSource(0); + + // Check that one entry is logged. + EXPECT_EQ(1U, ukm_service->entries_count()); + const ukm::UkmEntry* entry = ukm_service->GetEntry(0); + EXPECT_EQ(source->id(), entry->source_id()); + + ukm::Entry entry_proto; + entry->PopulateProto(&entry_proto); + EXPECT_EQ(source->id(), entry_proto.source_id()); + + // Check if there is an entry for card upload decisions. + EXPECT_EQ(base::HashMetricName(internal::kUKMCardUploadDecisionEntryName), + entry_proto.event_hash()); + EXPECT_EQ(1, entry_proto.metrics_size()); + + // Check that the expected upload decision is logged. + const ukm::Entry_Metric* metric = FindMetric( + internal::kUKMCardUploadDecisionMetricName, entry_proto.metrics()); + ASSERT_NE(nullptr, metric); + EXPECT_EQ(static_cast<int>(upload_decision), metric->value()); + } + protected: base::MessageLoop message_loop_; MockAutofillClient autofill_client_; @@ -1690,9 +1713,6 @@ TEST_F(AutofillManagerTest, // are no credit card suggestions and the promo is active. See the tests in // AutofillExternalDelegateTest that test whether the promo is added. TEST_F(AutofillManagerTest, GetCreditCardSuggestions_OnlySigninPromo) { - // Enable the signin promo feature with no impression limit. - EnableCreditCardSigninPromoFeatureWithLimit(0); - // Make sure there are no credit cards. personal_data_.ClearCreditCards(); @@ -4497,6 +4517,7 @@ TEST_F(AutofillManagerTest, FillInUpdatedExpirationDate) { #define MAYBE_UploadCreditCard UploadCreditCard #endif TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard) { + EnableUkmLogging(); personal_data_.ClearAutofillProfiles(); autofill_manager_->set_credit_card_upload_enabled(true); @@ -4528,6 +4549,8 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard) { // Verify that the correct histogram entry (and only that) was logged. histogram_tester.ExpectUniqueSample("Autofill.CardUploadDecisionExpanded", AutofillMetrics::UPLOAD_OFFERED, 1); + // Verify that the correct UKM was logged. + ExpectUniqueCardUploadDecisionUkm(AutofillMetrics::UPLOAD_OFFERED); } // TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot. @@ -4578,6 +4601,7 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_FeatureNotEnabled) { #define MAYBE_UploadCreditCard_CvcUnavailable UploadCreditCard_CvcUnavailable #endif TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_CvcUnavailable) { + EnableUkmLogging(); personal_data_.ClearAutofillProfiles(); autofill_manager_->set_credit_card_upload_enabled(true); @@ -4612,6 +4636,8 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_CvcUnavailable) { histogram_tester.ExpectUniqueSample( "Autofill.CardUploadDecisionExpanded", AutofillMetrics::UPLOAD_NOT_OFFERED_NO_CVC, 1); + // Verify that the correct UKM was logged. + ExpectUniqueCardUploadDecisionUkm(AutofillMetrics::UPLOAD_NOT_OFFERED_NO_CVC); rappor::TestRapporServiceImpl* rappor_service = autofill_client_.test_rappor_service(); @@ -4631,6 +4657,7 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_CvcUnavailable) { #define MAYBE_UploadCreditCard_CvcInvalidLength UploadCreditCard_CvcInvalidLength #endif TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_CvcInvalidLength) { + EnableUkmLogging(); personal_data_.ClearAutofillProfiles(); autofill_manager_->set_credit_card_upload_enabled(true); @@ -4665,6 +4692,8 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_CvcInvalidLength) { histogram_tester.ExpectUniqueSample( "Autofill.CardUploadDecisionExpanded", AutofillMetrics::UPLOAD_NOT_OFFERED_NO_CVC, 1); + // Verify that the correct UKM was logged. + ExpectUniqueCardUploadDecisionUkm(AutofillMetrics::UPLOAD_NOT_OFFERED_NO_CVC); rappor::TestRapporServiceImpl* rappor_service = autofill_client_.test_rappor_service(); @@ -4684,6 +4713,7 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_CvcInvalidLength) { #define MAYBE_UploadCreditCard_MultipleCvcFields UploadCreditCard_MultipleCvcFields #endif TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_MultipleCvcFields) { + EnableUkmLogging(); autofill_manager_->set_credit_card_upload_enabled(true); // Remove the profiles that were created in the TestPersonalDataManager @@ -4740,6 +4770,8 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_MultipleCvcFields) { histogram_tester.ExpectUniqueSample( "Autofill.CardUploadDecisionExpanded", AutofillMetrics::UPLOAD_OFFERED, 1); + // Verify that the correct UKM was logged. + ExpectUniqueCardUploadDecisionUkm(AutofillMetrics::UPLOAD_OFFERED); } // TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot. @@ -4749,6 +4781,7 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_MultipleCvcFields) { #define MAYBE_UploadCreditCard_NoProfileAvailable UploadCreditCard_NoProfileAvailable #endif TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_NoProfileAvailable) { + EnableUkmLogging(); personal_data_.ClearAutofillProfiles(); autofill_manager_->set_credit_card_upload_enabled(true); @@ -4777,6 +4810,9 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_NoProfileAvailable) { histogram_tester.ExpectUniqueSample( "Autofill.CardUploadDecisionExpanded", AutofillMetrics::UPLOAD_NOT_OFFERED_NO_ADDRESS, 1); + // Verify that the correct UKM was logged. + ExpectUniqueCardUploadDecisionUkm( + AutofillMetrics::UPLOAD_NOT_OFFERED_NO_ADDRESS); rappor::TestRapporServiceImpl* rappor_service = autofill_client_.test_rappor_service(); @@ -4791,11 +4827,65 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_NoProfileAvailable) { // TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot. #if defined(OS_ANDROID) +#define MAYBE_UploadCreditCard_CvcUnavailableAndNoProfileAvailable DISABLED_UploadCreditCard_CvcUnavailableAndNoProfileAvailable +#else +#define MAYBE_UploadCreditCard_CvcUnavailableAndNoProfileAvailable UploadCreditCard_CvcUnavailableAndNoProfileAvailable +#endif +TEST_F(AutofillManagerTest, + MAYBE_UploadCreditCard_CvcUnavailableAndNoProfileAvailable) { + EnableUkmLogging(); + personal_data_.ClearAutofillProfiles(); + autofill_manager_->set_credit_card_upload_enabled(true); + + // Don't fill or submit an 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(""); // CVC MISSING + + base::HistogramTester histogram_tester; + + // Neither a local save nor an upload should happen in this case. + // Note that AutofillManager should *check* for both no CVC and no address + // profile, but the no CVC case should have priority over being reported. + 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); + // Verify that the correct UKM was logged. + ExpectUniqueCardUploadDecisionUkm(AutofillMetrics::UPLOAD_NOT_OFFERED_NO_CVC); + + 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_NoNameAvailable DISABLED_UploadCreditCard_NoNameAvailable #else #define MAYBE_UploadCreditCard_NoNameAvailable UploadCreditCard_NoNameAvailable #endif TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_NoNameAvailable) { + EnableUkmLogging(); personal_data_.ClearAutofillProfiles(); autofill_manager_->set_credit_card_upload_enabled(true); @@ -4830,6 +4920,9 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_NoNameAvailable) { histogram_tester.ExpectUniqueSample( "Autofill.CardUploadDecisionExpanded", AutofillMetrics::UPLOAD_NOT_OFFERED_NO_NAME, 1); + // Verify that the correct UKM was logged. + ExpectUniqueCardUploadDecisionUkm( + AutofillMetrics::UPLOAD_NOT_OFFERED_NO_NAME); rappor::TestRapporServiceImpl* rappor_service = autofill_client_.test_rappor_service(); @@ -4849,6 +4942,7 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_NoNameAvailable) { #define MAYBE_UploadCreditCard_ZipCodesConflict UploadCreditCard_ZipCodesConflict #endif TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_ZipCodesConflict) { + EnableUkmLogging(); personal_data_.ClearAutofillProfiles(); autofill_manager_->set_credit_card_upload_enabled(true); @@ -4891,6 +4985,9 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_ZipCodesConflict) { histogram_tester.ExpectUniqueSample( "Autofill.CardUploadDecisionExpanded", AutofillMetrics::UPLOAD_NOT_OFFERED_CONFLICTING_ZIPS, 1); + // Verify that the correct UKM was logged. + ExpectUniqueCardUploadDecisionUkm( + AutofillMetrics::UPLOAD_NOT_OFFERED_CONFLICTING_ZIPS); } // TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot. @@ -4900,6 +4997,7 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_ZipCodesConflict) { #define MAYBE_UploadCreditCard_ZipCodesHavePrefixMatch UploadCreditCard_ZipCodesHavePrefixMatch #endif TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_ZipCodesHavePrefixMatch) { + EnableUkmLogging(); personal_data_.ClearAutofillProfiles(); autofill_manager_->set_credit_card_upload_enabled(true); @@ -4942,6 +5040,8 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_ZipCodesHavePrefixMatch) { histogram_tester.ExpectUniqueSample( "Autofill.CardUploadDecisionExpanded", AutofillMetrics::UPLOAD_OFFERED, 1); + // Verify that the correct UKM was logged. + ExpectUniqueCardUploadDecisionUkm(AutofillMetrics::UPLOAD_OFFERED); } // TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot. @@ -4951,6 +5051,7 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_ZipCodesHavePrefixMatch) { #define MAYBE_UploadCreditCard_NoZipCodeAvailable UploadCreditCard_NoZipCodeAvailable #endif TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_NoZipCodeAvailable) { + EnableUkmLogging(); personal_data_.ClearAutofillProfiles(); autofill_manager_->set_credit_card_upload_enabled(true); @@ -4992,6 +5093,9 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_NoZipCodeAvailable) { histogram_tester.ExpectUniqueSample( "Autofill.CardUploadDecisionExpanded", AutofillMetrics::UPLOAD_NOT_OFFERED_NO_ZIP_CODE, 1); + // Verify that the correct UKM was logged. + ExpectUniqueCardUploadDecisionUkm( + AutofillMetrics::UPLOAD_NOT_OFFERED_NO_ZIP_CODE); } // TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot. @@ -5001,6 +5105,7 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_NoZipCodeAvailable) { #define MAYBE_UploadCreditCard_NamesMatchLoosely UploadCreditCard_NamesMatchLoosely #endif TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_NamesMatchLoosely) { + EnableUkmLogging(); personal_data_.ClearAutofillProfiles(); autofill_manager_->set_credit_card_upload_enabled(true); @@ -5046,6 +5151,8 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_NamesMatchLoosely) { histogram_tester.ExpectUniqueSample( "Autofill.CardUploadDecisionExpanded", AutofillMetrics::UPLOAD_OFFERED, 1); + // Verify that the correct UKM was logged. + ExpectUniqueCardUploadDecisionUkm(AutofillMetrics::UPLOAD_OFFERED); } // TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot. @@ -5055,6 +5162,7 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_NamesMatchLoosely) { #define MAYBE_UploadCreditCard_NamesHaveToMatch UploadCreditCard_NamesHaveToMatch #endif TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_NamesHaveToMatch) { + EnableUkmLogging(); personal_data_.ClearAutofillProfiles(); autofill_manager_->set_credit_card_upload_enabled(true); @@ -5097,6 +5205,9 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_NamesHaveToMatch) { histogram_tester.ExpectUniqueSample( "Autofill.CardUploadDecisionExpanded", AutofillMetrics::UPLOAD_NOT_OFFERED_CONFLICTING_NAMES, 1); + // Verify that the correct UKM was logged. + ExpectUniqueCardUploadDecisionUkm( + AutofillMetrics::UPLOAD_NOT_OFFERED_CONFLICTING_NAMES); rappor::TestRapporServiceImpl* rappor_service = autofill_client_.test_rappor_service(); @@ -5116,6 +5227,7 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_NamesHaveToMatch) { #define MAYBE_UploadCreditCard_UploadDetailsFails UploadCreditCard_UploadDetailsFails #endif TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_UploadDetailsFails) { + EnableUkmLogging(); personal_data_.ClearAutofillProfiles(); autofill_manager_->set_credit_card_upload_enabled(true); @@ -5154,6 +5266,9 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_UploadDetailsFails) { histogram_tester.ExpectUniqueSample( "Autofill.CardUploadDecisionExpanded", AutofillMetrics::UPLOAD_NOT_OFFERED_GET_UPLOAD_DETAILS_FAILED, 1); + // Verify that the correct UKM was logged. + ExpectUniqueCardUploadDecisionUkm( + AutofillMetrics::UPLOAD_NOT_OFFERED_GET_UPLOAD_DETAILS_FAILED); } // Verify that typing "gmail" will match "theking@gmail.com" and @@ -5271,11 +5386,12 @@ TEST_F(AutofillManagerTest, NoCreditCardSuggestionsForNonPrefixTokenMatch) { } // Test that ShouldShowCreditCardSigninPromo behaves as expected for a credit -// card form, with no impression limit and the feature enabled. +// card form with an impression limit of three and no impressions yet. TEST_F(AutofillManagerTest, - ShouldShowCreditCardSigninPromo_CreditCardField_NoLimit) { - // Enable the feature with no impression limit. - EnableCreditCardSigninPromoFeatureWithLimit(0); + ShouldShowCreditCardSigninPromo_CreditCardField_UnmetLimit) { + // No impressions yet. + ASSERT_EQ(0, autofill_client_.GetPrefs()->GetInteger( + prefs::kAutofillCreditCardSigninPromoImpressionCount)); // Set up our form data. FormData form; @@ -5286,21 +5402,29 @@ TEST_F(AutofillManagerTest, FormFieldData field; test::CreateTestFormField("Name on Card", "nameoncard", "", "text", &field); - // The result will depend on what ShouldShowSigninPromo(). We control its - // output and verify that both cases behave as expected. + // The mock implementation of ShouldShowSigninPromo() will return true here, + // creating an impression, and false below, preventing an impression. EXPECT_CALL(autofill_client_, ShouldShowSigninPromo()) .WillOnce(testing::Return(true)); EXPECT_TRUE(autofill_manager_->ShouldShowCreditCardSigninPromo(form, field)); + // Expect to now have an impression. + EXPECT_EQ(1, autofill_client_.GetPrefs()->GetInteger( + prefs::kAutofillCreditCardSigninPromoImpressionCount)); + EXPECT_CALL(autofill_client_, ShouldShowSigninPromo()) .WillOnce(testing::Return(false)); EXPECT_FALSE(autofill_manager_->ShouldShowCreditCardSigninPromo(form, field)); + + // No additional impression. + EXPECT_EQ(1, autofill_client_.GetPrefs()->GetInteger( + prefs::kAutofillCreditCardSigninPromoImpressionCount)); } -// Test that ShouldShowCreditCardSigninPromo doesn't show for a credit card form -// when the feature is off. +// Test that ShouldShowCreditCardSigninPromo behaves as expected for a credit +// card form with an impression limit that has been attained already. TEST_F(AutofillManagerTest, - ShouldShowCreditCardSigninPromo_CreditCardField_FeatureOff) { + ShouldShowCreditCardSigninPromo_CreditCardField_WithAttainedLimit) { // Set up our form data. FormData form; CreateTestCreditCardFormData(&form, true, false); @@ -5310,69 +5434,73 @@ TEST_F(AutofillManagerTest, FormFieldData field; test::CreateTestFormField("Name on Card", "nameoncard", "", "text", &field); - // Returns false without calling ShouldShowSigninPromo(). - EXPECT_CALL(autofill_client_, ShouldShowSigninPromo()).Times(0); + // Set the impression count to the same value as the limit. + autofill_client_.GetPrefs()->SetInteger( + prefs::kAutofillCreditCardSigninPromoImpressionCount, + kCreditCardSigninPromoImpressionLimit); + + // Both calls will now return false, regardless of the mock implementation of + // ShouldShowSigninPromo(). + EXPECT_CALL(autofill_client_, ShouldShowSigninPromo()) + .WillOnce(testing::Return(true)); EXPECT_FALSE(autofill_manager_->ShouldShowCreditCardSigninPromo(form, field)); + + EXPECT_CALL(autofill_client_, ShouldShowSigninPromo()) + .WillOnce(testing::Return(false)); + EXPECT_FALSE(autofill_manager_->ShouldShowCreditCardSigninPromo(form, field)); + + // Number of impressions stay the same. + EXPECT_EQ(kCreditCardSigninPromoImpressionLimit, + autofill_client_.GetPrefs()->GetInteger( + prefs::kAutofillCreditCardSigninPromoImpressionCount)); } // Test that ShouldShowCreditCardSigninPromo behaves as expected for a credit -// card form with an impression limit and no impressions yet. +// card form on a non-secure page. TEST_F(AutofillManagerTest, - ShouldShowCreditCardSigninPromo_CreditCardField_UnmetLimit) { - // Enable the feature with an impression limit. - EnableCreditCardSigninPromoFeatureWithLimit(10); - // No impressions yet. - ASSERT_EQ(0, autofill_client_.GetPrefs()->GetInteger( - prefs::kAutofillCreditCardSigninPromoImpressionCount)); - + ShouldShowCreditCardSigninPromo_CreditCardField_NonSecureContext) { // Set up our form data. FormData form; - CreateTestCreditCardFormData(&form, true, false); + CreateTestCreditCardFormData(&form, false, false); + form.origin = GURL("http://myform.com/form.html"); + form.action = GURL("https://myform.com/submit.html"); + autofill_client_.set_form_origin(form.origin); std::vector<FormData> forms(1, form); FormsSeen(forms); FormFieldData field; test::CreateTestFormField("Name on Card", "nameoncard", "", "text", &field); - // The mock implementation of ShouldShowSigninPromo() will return true here, - // creating an impression, and false below, preventing an impression. + // Both calls will now return false, regardless of the mock implementation of + // ShouldShowSigninPromo(). EXPECT_CALL(autofill_client_, ShouldShowSigninPromo()) .WillOnce(testing::Return(true)); - EXPECT_TRUE(autofill_manager_->ShouldShowCreditCardSigninPromo(form, field)); - - // Expect to now have an impression. - EXPECT_EQ(1, autofill_client_.GetPrefs()->GetInteger( - prefs::kAutofillCreditCardSigninPromoImpressionCount)); + EXPECT_FALSE(autofill_manager_->ShouldShowCreditCardSigninPromo(form, field)); EXPECT_CALL(autofill_client_, ShouldShowSigninPromo()) .WillOnce(testing::Return(false)); EXPECT_FALSE(autofill_manager_->ShouldShowCreditCardSigninPromo(form, field)); - // No additional impression. - EXPECT_EQ(1, autofill_client_.GetPrefs()->GetInteger( + // Number of impressions should remain at zero. + EXPECT_EQ(0, autofill_client_.GetPrefs()->GetInteger( prefs::kAutofillCreditCardSigninPromoImpressionCount)); } // Test that ShouldShowCreditCardSigninPromo behaves as expected for a credit -// card form with an impression limit that has been attained already. +// card form targeting a non-secure page. TEST_F(AutofillManagerTest, - ShouldShowCreditCardSigninPromo_CreditCardField_WithAttainedLimit) { - // Enable the feature with an impression limit. - EnableCreditCardSigninPromoFeatureWithLimit(10); - + ShouldShowCreditCardSigninPromo_CreditCardField_NonSecureAction) { // Set up our form data. FormData form; - CreateTestCreditCardFormData(&form, true, false); + CreateTestCreditCardFormData(&form, false, false); + form.origin = GURL("https://myform.com/form.html"); + form.action = GURL("http://myform.com/submit.html"); std::vector<FormData> forms(1, form); FormsSeen(forms); FormFieldData field; test::CreateTestFormField("Name on Card", "nameoncard", "", "text", &field); - // Set the impression count to the same value as the limit. - autofill_client_.GetPrefs()->SetInteger( - prefs::kAutofillCreditCardSigninPromoImpressionCount, 10); - // Both calls will now return false, regardless of the mock implementation of // ShouldShowSigninPromo(). EXPECT_CALL(autofill_client_, ShouldShowSigninPromo()) @@ -5383,9 +5511,9 @@ TEST_F(AutofillManagerTest, .WillOnce(testing::Return(false)); EXPECT_FALSE(autofill_manager_->ShouldShowCreditCardSigninPromo(form, field)); - // Number of impressions stay the same. - EXPECT_EQ(10, autofill_client_.GetPrefs()->GetInteger( - prefs::kAutofillCreditCardSigninPromoImpressionCount)); + // Number of impressions should remain at zero. + EXPECT_EQ(0, autofill_client_.GetPrefs()->GetInteger( + prefs::kAutofillCreditCardSigninPromoImpressionCount)); } // Test that ShouldShowCreditCardSigninPromo behaves as expected for an address diff --git a/chromium/components/autofill/core/browser/autofill_metrics.cc b/chromium/components/autofill/core/browser/autofill_metrics.cc index 66a7bfcc967..239334c5e66 100644 --- a/chromium/components/autofill/core/browser/autofill_metrics.cc +++ b/chromium/components/autofill/core/browser/autofill_metrics.cc @@ -11,9 +11,16 @@ #include "base/metrics/sparse_histogram.h" #include "base/metrics/user_metrics.h" #include "base/time/time.h" +#include "components/autofill/core/browser/autofill_experiments.h" #include "components/autofill/core/browser/autofill_type.h" #include "components/autofill/core/browser/form_structure.h" #include "components/autofill/core/common/form_data.h" +#include "components/ukm/ukm_entry_builder.h" + +namespace internal { +const char kUKMCardUploadDecisionEntryName[] = "Autofill.CardUploadDecision"; +const char kUKMCardUploadDecisionMetricName[] = "UploadDecision"; +} // namespace internal namespace autofill { @@ -676,11 +683,56 @@ void AutofillMetrics::LogIsQueriedCreditCardFormSecure(bool is_secure) { } // static +void AutofillMetrics::LogWalletAddressConversionType( + WalletAddressConversionType type) { + DCHECK_LT(type, NUM_CONVERTED_ADDRESS_CONVERSION_TYPES); + UMA_HISTOGRAM_ENUMERATION("Autofill.WalletAddressConversionType", type, + NUM_CONVERTED_ADDRESS_CONVERSION_TYPES); +} + +// static void AutofillMetrics::LogShowedHttpNotSecureExplanation() { base::RecordAction( base::UserMetricsAction("Autofill_ShowedHttpNotSecureExplanation")); } +// static +void AutofillMetrics::LogCardUploadDecisionUkm( + ukm::UkmService* ukm_service, + const GURL& url, + AutofillMetrics::CardUploadDecisionMetric upload_decision) { + if (upload_decision >= AutofillMetrics::NUM_CARD_UPLOAD_DECISION_METRICS) + return; + + // Set up as a map because the follow-up CL will add more metrics. + std::map<std::string, int> metrics = { + {internal::kUKMCardUploadDecisionMetricName, + static_cast<int>(upload_decision)}}; + LogUkm(ukm_service, url, internal::kUKMCardUploadDecisionEntryName, metrics); +} + +// static +bool AutofillMetrics::LogUkm(ukm::UkmService* ukm_service, + const GURL& url, + const std::string& ukm_entry_name, + const std::map<std::string, int>& metrics) { + if (!IsUkmLoggingEnabled() || !ukm_service || !url.is_valid() || + metrics.empty()) { + return false; + } + + int32_t source_id = ukm_service->GetNewSourceID(); + ukm_service->UpdateSourceURL(source_id, url); + std::unique_ptr<ukm::UkmEntryBuilder> builder = + ukm_service->GetEntryBuilder(source_id, ukm_entry_name.c_str()); + + for (auto it = metrics.begin(); it != metrics.end(); ++it) { + builder->AddMetric(it->first.c_str(), it->second); + } + + return true; +} + AutofillMetrics::FormEventLogger::FormEventLogger(bool is_for_credit_card) : is_for_credit_card_(is_for_credit_card), is_server_data_available_(false), diff --git a/chromium/components/autofill/core/browser/autofill_metrics.h b/chromium/components/autofill/core/browser/autofill_metrics.h index 6fe8a34ebc3..d0aba8221f6 100644 --- a/chromium/components/autofill/core/browser/autofill_metrics.h +++ b/chromium/components/autofill/core/browser/autofill_metrics.h @@ -17,7 +17,17 @@ namespace base { class TimeDelta; -} +} // namespace base + +namespace ukm { +class UkmService; +} // namespace ukm + +namespace internal { +// Name constants are exposed here so they can be referenced from tests. +extern const char kUKMCardUploadDecisionEntryName[]; +extern const char kUKMCardUploadDecisionMetricName[]; +} // namespace internal namespace autofill { @@ -518,6 +528,15 @@ class AutofillMetrics { NUM_WALLET_REQUIRED_ACTIONS }; + // For mesuring how wallet addresses are converted to local profiles. + enum WalletAddressConversionType : int { + // The converted wallet address was merged into an existing local profile. + CONVERTED_ADDRESS_MERGED, + // The converted wallet address was added as a new local profile. + CONVERTED_ADDRESS_ADDED, + NUM_CONVERTED_ADDRESS_CONVERSION_TYPES + }; + static void LogCardUploadDecisionMetric(CardUploadDecisionMetric metric); static void LogCreditCardInfoBarMetric(InfoBarMetric metric, bool is_uploading); @@ -663,10 +682,28 @@ class AutofillMetrics { // context. static void LogIsQueriedCreditCardFormSecure(bool is_secure); + // Log how the converted wallet address was added to the local autofill + // profiles. + static void LogWalletAddressConversionType(WalletAddressConversionType type); + // This should be called when the user selects the Form-Not-Secure warning // suggestion to show an explanation of the warning. static void LogShowedHttpNotSecureExplanation(); + // Logs the card upload decision ukm based on the specified |url| and + // |upload_decision|. + static void LogCardUploadDecisionUkm( + ukm::UkmService* ukm_service, + const GURL& url, + AutofillMetrics::CardUploadDecisionMetric upload_decision); + + // Logs the the |ukm_entry_name| with the specified |url| and the specified + // |metrics|. Returns whether the ukm was sucessfully logged. + static bool LogUkm(ukm::UkmService* ukm_service, + const GURL& url, + const std::string& ukm_entry_name, + const std::map<std::string, int>& metrics); + // Utility to autofill form events in the relevant histograms depending on // the presence of server and/or local data. class FormEventLogger { diff --git a/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc b/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc index f9799f5e8af..36039f5f382 100644 --- a/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc @@ -6,17 +6,22 @@ #include <stddef.h> +#include <map> #include <memory> #include <vector> +#include "base/feature_list.h" #include "base/macros.h" #include "base/memory/ptr_util.h" +#include "base/metrics/metrics_hashes.h" #include "base/run_loop.h" #include "base/strings/string16.h" #include "base/strings/utf_string_conversions.h" #include "base/test/histogram_tester.h" +#include "base/test/scoped_feature_list.h" #include "base/test/user_action_tester.h" #include "base/time/time.h" +#include "components/autofill/core/browser/autofill_experiments.h" #include "components/autofill/core/browser/autofill_external_delegate.h" #include "components/autofill/core/browser/autofill_manager.h" #include "components/autofill/core/browser/autofill_test_utils.h" @@ -28,12 +33,16 @@ #include "components/autofill/core/browser/webdata/autofill_webdata_service.h" #include "components/autofill/core/common/form_data.h" #include "components/autofill/core/common/form_field_data.h" +#include "components/metrics/proto/ukm/entry.pb.h" #include "components/prefs/pref_service.h" #include "components/rappor/test_rappor_service.h" #include "components/signin/core/browser/account_tracker_service.h" #include "components/signin/core/browser/fake_signin_manager.h" #include "components/signin/core/browser/test_signin_client.h" #include "components/signin/core/common/signin_pref_names.h" +#include "components/ukm/test_ukm_service.h" +#include "components/ukm/ukm_entry.h" +#include "components/ukm/ukm_source.h" #include "components/webdata/common/web_data_results.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -125,33 +134,25 @@ class TestPersonalDataManager : public PersonalDataManager { autofill_enabled_ = autofill_enabled; } - // Removes all existing profiles and creates 0 or 1 local profiles and 0 or 1 - // server profile according to the parameters. - void RecreateProfiles(bool include_local_profile, - bool include_server_profile) { + // Removes all existing profiles + void ClearProfiles() { web_profiles_.clear(); - server_profiles_.clear(); - if (include_local_profile) { - std::unique_ptr<AutofillProfile> profile = - base::MakeUnique<AutofillProfile>(); - test::SetProfileInfo(profile.get(), "Elvis", "Aaron", "Presley", - "theking@gmail.com", "RCA", - "3734 Elvis Presley Blvd.", "Apt. 10", "Memphis", - "Tennessee", "38116", "US", "12345678901"); - profile->set_guid("00000000-0000-0000-0000-000000000001"); - web_profiles_.push_back(std::move(profile)); - } - if (include_server_profile) { - std::unique_ptr<AutofillProfile> profile = - base::MakeUnique<AutofillProfile>(AutofillProfile::SERVER_PROFILE, - "server_id"); - test::SetProfileInfo(profile.get(), "Charles", "Hardin", "Holley", - "buddy@gmail.com", "Decca", "123 Apple St.", - "unit 6", "Lubbock", "Texas", "79401", "US", - "2345678901"); - profile->set_guid("00000000-0000-0000-0000-000000000002"); - server_profiles_.push_back(std::move(profile)); - } + Refresh(); + } + + // Removes all existing profiles and creates one profile. + void RecreateProfile() { + web_profiles_.clear(); + + std::unique_ptr<AutofillProfile> profile = + base::MakeUnique<AutofillProfile>(); + test::SetProfileInfo(profile.get(), "Elvis", "Aaron", "Presley", + "theking@gmail.com", "RCA", "3734 Elvis Presley Blvd.", + "Apt. 10", "Memphis", "Tennessee", "38116", "US", + "12345678901"); + profile->set_guid("00000000-0000-0000-0000-000000000001"); + web_profiles_.push_back(std::move(profile)); + Refresh(); } @@ -304,6 +305,17 @@ class TestAutofillManager : public AutofillManager { DISALLOW_COPY_AND_ASSIGN(TestAutofillManager); }; +// Finds the specified UKM metric by |name| in the specified UKM |metrics|. +const ukm::Entry_Metric* FindMetric( + const char* name, + const google::protobuf::RepeatedPtrField<ukm::Entry_Metric>& metrics) { + for (const auto& metric : metrics) { + if (metric.metric_hash() == base::HashMetricName(name)) + return &metric; + } + return nullptr; +} + } // namespace // This is defined in the autofill_metrics.cc implementation file. @@ -319,6 +331,7 @@ class AutofillMetricsTest : public testing::Test { protected: void EnableWalletSync(); + void EnableUkmLogging(); base::MessageLoop message_loop_; TestAutofillClient autofill_client_; @@ -329,6 +342,7 @@ class AutofillMetricsTest : public testing::Test { std::unique_ptr<TestAutofillManager> autofill_manager_; std::unique_ptr<TestPersonalDataManager> personal_data_; std::unique_ptr<AutofillExternalDelegate> external_delegate_; + base::test::ScopedFeatureList scoped_feature_list_; }; AutofillMetricsTest::~AutofillMetricsTest() { @@ -385,6 +399,10 @@ void AutofillMetricsTest::EnableWalletSync() { signin_manager_->SetAuthenticatedAccountInfo("12345", "syncuser@example.com"); } +void AutofillMetricsTest::EnableUkmLogging() { + scoped_feature_list_.InitAndEnableFeature(kAutofillUkmLogging); +} + // Test that we log quality metrics appropriately. TEST_F(AutofillMetricsTest, QualityMetrics) { // Set up our form data. @@ -1741,9 +1759,8 @@ TEST_F(AutofillMetricsTest, CreditCardCheckoutFlowUserActions) { // Test that the profile checkout flow user actions are correctly logged. TEST_F(AutofillMetricsTest, ProfileCheckoutFlowUserActions) { - // Create profiles. - personal_data_->RecreateProfiles(true /* include_local_profile */, - false /* include_server_profile */); + // Create a profile. + personal_data_->RecreateProfile(); // Set up our form data. FormData form; @@ -1935,8 +1952,7 @@ TEST_F(AutofillMetricsTest, QueriedCreditCardFormIsSecure) { // Tests that the Autofill_PolledProfileSuggestions user action is only logged // once if the field is queried repeatedly. TEST_F(AutofillMetricsTest, PolledProfileSuggestions_DebounceLogs) { - personal_data_->RecreateProfiles(true /* include_local_profile */, - false /* include_server_profile */); + personal_data_->RecreateProfile(); // Set up the form data. FormData form; @@ -2875,9 +2891,8 @@ TEST_F(AutofillMetricsTest, AddressInteractedFormEvents) { // Test that we log suggestion shown form events for address. TEST_F(AutofillMetricsTest, AddressShownFormEvents) { EnableWalletSync(); - // Creating all kinds of profiles. - personal_data_->RecreateProfiles(true /* include_local_profile */, - true /* include_server_profile */); + // Create a profile. + personal_data_->RecreateProfile(); // Set up our form data. FormData form; form.name = ASCIIToUTF16("TestForm"); @@ -2950,9 +2965,8 @@ TEST_F(AutofillMetricsTest, AddressShownFormEvents) { // Test that we log filled form events for address. TEST_F(AutofillMetricsTest, AddressFilledFormEvents) { EnableWalletSync(); - // Creating all kinds of profiles. - personal_data_->RecreateProfiles(true /* include_local_profile */, - true /* include_server_profile */); + // Create a profile. + personal_data_->RecreateProfile(); // Set up our form data. FormData form; form.name = ASCIIToUTF16("TestForm"); @@ -2995,25 +3009,6 @@ TEST_F(AutofillMetricsTest, AddressFilledFormEvents) { autofill_manager_->AddSeenForm(form, field_types, field_types); { - // Simulating selecting/filling a server profile suggestion. - base::HistogramTester histogram_tester; - std::string guid("00000000-0000-0000-0000-000000000002"); // server profile - autofill_manager_->FillOrPreviewForm( - AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.front(), - autofill_manager_->MakeFrontendID(std::string(), guid)); - histogram_tester.ExpectBucketCount( - "Autofill.FormEvents.Address", - AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_FILLED, 1); - histogram_tester.ExpectBucketCount( - "Autofill.FormEvents.Address", - AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_FILLED_ONCE, 1); - } - - // Reset the autofill manager state. - autofill_manager_->Reset(); - autofill_manager_->AddSeenForm(form, field_types, field_types); - - { // Simulating selecting/filling a local profile suggestion. base::HistogramTester histogram_tester; std::string guid("00000000-0000-0000-0000-000000000001"); // local profile @@ -3035,9 +3030,8 @@ TEST_F(AutofillMetricsTest, AddressFilledFormEvents) { // Test that we log submitted form events for address. TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) { EnableWalletSync(); - // Creating all kinds of profiles. - personal_data_->RecreateProfiles(true /* include_local_profile */, - true /* include_server_profile */); + // Create a profile. + personal_data_->RecreateProfile(); // Set up our form data. FormData form; form.name = ASCIIToUTF16("TestForm"); @@ -3117,27 +3111,6 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) { autofill_manager_->AddSeenForm(form, field_types, field_types); { - // Simulating submission with filled server data. - base::HistogramTester histogram_tester; - autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF()); - std::string guid("00000000-0000-0000-0000-000000000002"); // server profile - autofill_manager_->FillOrPreviewForm( - AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.front(), - autofill_manager_->MakeFrontendID(std::string(), guid)); - autofill_manager_->SubmitForm(form, TimeTicks::Now()); - histogram_tester.ExpectBucketCount( - "Autofill.FormEvents.Address", - AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 1); - histogram_tester.ExpectBucketCount( - "Autofill.FormEvents.Address", - AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 1); - } - - // Reset the autofill manager state. - autofill_manager_->Reset(); - autofill_manager_->AddSeenForm(form, field_types, field_types); - - { // Simulating multiple submissions. base::HistogramTester histogram_tester; autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF()); @@ -3221,9 +3194,8 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) { // metrics. TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) { EnableWalletSync(); - // Creating all kinds of profiles. - personal_data_->RecreateProfiles(true /* include_local_profile */, - true /* include_server_profile */); + // Create a profile. + personal_data_->RecreateProfile(); // Set up our form data. FormData form; form.name = ASCIIToUTF16("TestForm"); @@ -3303,27 +3275,6 @@ TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) { autofill_manager_->AddSeenForm(form, field_types, field_types); { - // Simulating submission with filled server data. - base::HistogramTester histogram_tester; - autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF()); - std::string guid("00000000-0000-0000-0000-000000000002"); // server profile - autofill_manager_->FillOrPreviewForm( - AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.front(), - autofill_manager_->MakeFrontendID(std::string(), guid)); - autofill_manager_->WillSubmitForm(form, TimeTicks::Now()); - histogram_tester.ExpectBucketCount( - "Autofill.FormEvents.Address", - AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 1); - histogram_tester.ExpectBucketCount( - "Autofill.FormEvents.Address", - AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0); - } - - // Reset the autofill manager state. - autofill_manager_->Reset(); - autofill_manager_->AddSeenForm(form, field_types, field_types); - - { // Simulating multiple submissions. base::HistogramTester histogram_tester; autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF()); @@ -3545,8 +3496,7 @@ TEST_F(AutofillMetricsTest, AddressFormEventsAreSegmented) { // Simulate having seen this form on page load. // |form_structure| will be owned by |autofill_manager_|. autofill_manager_->AddSeenForm(form, field_types, field_types); - personal_data_->RecreateProfiles(false /* include_local_profile */, - false /* include_server_profile */); + personal_data_->ClearProfiles(); { // Simulate activating the autofill popup for the street field. @@ -3560,8 +3510,7 @@ TEST_F(AutofillMetricsTest, AddressFormEventsAreSegmented) { // Reset the autofill manager state. autofill_manager_->Reset(); autofill_manager_->AddSeenForm(form, field_types, field_types); - personal_data_->RecreateProfiles(true /* include_local_profile */, - false /* include_server_profile */); + personal_data_->RecreateProfile(); { // Simulate activating the autofill popup for the street field. @@ -3571,36 +3520,6 @@ TEST_F(AutofillMetricsTest, AddressFormEventsAreSegmented) { "Autofill.FormEvents.Address.WithOnlyLocalData", AutofillMetrics::FORM_EVENT_INTERACTED_ONCE, 1); } - - // Reset the autofill manager state. - autofill_manager_->Reset(); - autofill_manager_->AddSeenForm(form, field_types, field_types); - personal_data_->RecreateProfiles(false /* include_local_profile */, - true /* include_server_profile */); - - { - // Simulate activating the autofill popup for the street field. - base::HistogramTester histogram_tester; - autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF()); - histogram_tester.ExpectUniqueSample( - "Autofill.FormEvents.Address.WithOnlyServerData", - AutofillMetrics::FORM_EVENT_INTERACTED_ONCE, 1); - } - - // Reset the autofill manager state. - autofill_manager_->Reset(); - autofill_manager_->AddSeenForm(form, field_types, field_types); - personal_data_->RecreateProfiles(true /* include_local_profile */, - true /* include_server_profile */); - - { - // Simulate activating the autofill popup for the street field. - base::HistogramTester histogram_tester; - autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF()); - histogram_tester.ExpectUniqueSample( - "Autofill.FormEvents.Address.WithBothServerAndLocalData", - AutofillMetrics::FORM_EVENT_INTERACTED_ONCE, 1); - } } @@ -4447,4 +4366,94 @@ TEST_F(AutofillMetricsTest, } } +// Tests that logging a UKM works as expected. +TEST_F(AutofillMetricsTest, RecordCardUploadDecisionMetric) { + EnableUkmLogging(); + ukm::UkmServiceTestingHarness ukm_service_test_harness; + GURL url("https://www.google.com"); + int upload_decision = 1; + std::map<std::string, int> metrics; + metrics.insert(std::make_pair(internal::kUKMCardUploadDecisionMetricName, + upload_decision)); + + EXPECT_TRUE(AutofillMetrics::LogUkm( + ukm_service_test_harness.test_ukm_service(), url, + internal::kUKMCardUploadDecisionEntryName, metrics)); + + // Make sure that the UKM was logged correctly. + ukm::TestUkmService* ukm_service = + ukm_service_test_harness.test_ukm_service(); + + ASSERT_EQ(1U, ukm_service->sources_count()); + const ukm::UkmSource* source = ukm_service->GetSource(0); + EXPECT_EQ(url.spec(), source->url().spec()); + + EXPECT_EQ(1U, ukm_service->entries_count()); + const ukm::UkmEntry* entry = ukm_service->GetEntry(0); + EXPECT_EQ(source->id(), entry->source_id()); + + // Make sure that an card upload decision entry was logged. + ukm::Entry entry_proto; + entry->PopulateProto(&entry_proto); + EXPECT_EQ(source->id(), entry_proto.source_id()); + EXPECT_EQ(base::HashMetricName(internal::kUKMCardUploadDecisionEntryName), + entry_proto.event_hash()); + EXPECT_EQ(1, entry_proto.metrics_size()); + + // Make sure that the correct upload decision was logged. + const ukm::Entry_Metric* metric = FindMetric( + internal::kUKMCardUploadDecisionMetricName, entry_proto.metrics()); + ASSERT_NE(nullptr, metric); + EXPECT_EQ(upload_decision, metric->value()); +} + +// Tests that no UKM is logged when the URL is not valid. +TEST_F(AutofillMetricsTest, RecordCardUploadDecisionMetric_InvalidUrl) { + EnableUkmLogging(); + ukm::UkmServiceTestingHarness ukm_service_test_harness; + GURL url(""); + std::map<std::string, int> metrics; + metrics.insert(std::make_pair("metric", 1)); + + EXPECT_FALSE(AutofillMetrics::LogUkm( + ukm_service_test_harness.test_ukm_service(), url, "test_ukm", metrics)); + EXPECT_EQ(0U, ukm_service_test_harness.test_ukm_service()->sources_count()); +} + +// Tests that no UKM is logged when the metrics map is empty. +TEST_F(AutofillMetricsTest, RecordCardUploadDecisionMetric_NoMetrics) { + EnableUkmLogging(); + ukm::UkmServiceTestingHarness ukm_service_test_harness; + GURL url("https://www.google.com"); + std::map<std::string, int> metrics; + + EXPECT_FALSE(AutofillMetrics::LogUkm( + ukm_service_test_harness.test_ukm_service(), url, "test_ukm", metrics)); + EXPECT_EQ(0U, ukm_service_test_harness.test_ukm_service()->sources_count()); +} + +// Tests that no UKM is logged when the ukm service is null. +TEST_F(AutofillMetricsTest, RecordCardUploadDecisionMetric_NoUkmService) { + EnableUkmLogging(); + ukm::UkmServiceTestingHarness ukm_service_test_harness; + GURL url("https://www.google.com"); + std::map<std::string, int> metrics; + metrics.insert(std::make_pair("metric", 1)); + + EXPECT_FALSE(AutofillMetrics::LogUkm(nullptr, url, "test_ukm", metrics)); + ASSERT_EQ(0U, ukm_service_test_harness.test_ukm_service()->sources_count()); +} + +// Tests that no UKM is logged when the ukm logging feature is disabled. +TEST_F(AutofillMetricsTest, RecordCardUploadDecisionMetric_FeatureDisabled) { + ukm::UkmServiceTestingHarness ukm_service_test_harness; + GURL url("https://www.google.com"); + std::map<std::string, int> metrics; + metrics.insert(std::make_pair("metric", 1)); + + EXPECT_FALSE(AutofillMetrics::LogUkm( + ukm_service_test_harness.test_ukm_service(), url, "test_ukm", metrics)); + EXPECT_EQ(0U, ukm_service_test_harness.test_ukm_service()->sources_count()); +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_profile.cc b/chromium/components/autofill/core/browser/autofill_profile.cc index 9630d5b188b..1828497e688 100644 --- a/chromium/components/autofill/core/browser/autofill_profile.cc +++ b/chromium/components/autofill/core/browser/autofill_profile.cc @@ -33,9 +33,10 @@ #include "components/autofill/core/browser/phone_number_i18n.h" #include "components/autofill/core/browser/state_names.h" #include "components/autofill/core/browser/validation.h" +#include "components/autofill/core/common/autofill_clock.h" #include "components/autofill/core/common/autofill_l10n_util.h" #include "components/autofill/core/common/form_field_data.h" -#include "grit/components_strings.h" +#include "components/strings/grit/components_strings.h" #include "third_party/icu/source/common/unicode/uchar.h" #include "third_party/icu/source/common/unicode/utypes.h" #include "third_party/icu/source/i18n/unicode/translit.h" @@ -702,7 +703,7 @@ void AutofillProfile::GenerateServerProfileIdentifier() { void AutofillProfile::RecordAndLogUse() { UMA_HISTOGRAM_COUNTS_1000("Autofill.DaysSinceLastUse.Profile", - (base::Time::Now() - use_date()).InDays()); + (AutofillClock::Now() - use_date()).InDays()); RecordUse(); } diff --git a/chromium/components/autofill/core/browser/autofill_save_card_infobar_delegate_mobile.cc b/chromium/components/autofill/core/browser/autofill_save_card_infobar_delegate_mobile.cc index 4ac1475d133..fe2e1d3f566 100644 --- a/chromium/components/autofill/core/browser/autofill_save_card_infobar_delegate_mobile.cc +++ b/chromium/components/autofill/core/browser/autofill_save_card_infobar_delegate_mobile.cc @@ -11,10 +11,10 @@ #include "components/autofill/core/browser/credit_card.h" #include "components/autofill/core/browser/legal_message_line.h" #include "components/autofill/core/common/autofill_constants.h" +#include "components/grit/components_scaled_resources.h" #include "components/infobars/core/infobar.h" #include "components/infobars/core/infobar_manager.h" -#include "grit/components_scaled_resources.h" -#include "grit/components_strings.h" +#include "components/strings/grit/components_strings.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/window_open_disposition.h" #include "ui/gfx/vector_icons_public.h" diff --git a/chromium/components/autofill/core/browser/autofill_test_utils.cc b/chromium/components/autofill/core/browser/autofill_test_utils.cc index 6994b6faa76..78b1f0e09ed 100644 --- a/chromium/components/autofill/core/browser/autofill_test_utils.cc +++ b/chromium/components/autofill/core/browser/autofill_test_utils.cc @@ -341,8 +341,7 @@ void FillUploadField(AutofillUploadContents::Field* field, const char* name, const char* control_type, const char* autocomplete, - unsigned autofill_type, - const char* css_classes) { + unsigned autofill_type) { field->set_signature(signature); if (name) field->set_name(name); @@ -351,8 +350,6 @@ void FillUploadField(AutofillUploadContents::Field* field, if (autocomplete) field->set_autocomplete(autocomplete); field->set_autofill_type(autofill_type); - if (css_classes) - field->set_css_classes(css_classes); } void FillQueryField(AutofillQueryContents::Form::Field* field, diff --git a/chromium/components/autofill/core/browser/autofill_test_utils.h b/chromium/components/autofill/core/browser/autofill_test_utils.h index 4912732cb9e..3e6be716ff4 100644 --- a/chromium/components/autofill/core/browser/autofill_test_utils.h +++ b/chromium/components/autofill/core/browser/autofill_test_utils.h @@ -132,8 +132,7 @@ void FillUploadField(AutofillUploadContents::Field* field, const char* name, const char* control_type, const char* autocomplete, - unsigned autofill_type, - const char* css_classes); + unsigned autofill_type); // Fills the query form |field| with the information passed by parameter. If the // value of a const char* parameter is NULL, the corresponding attribute won't diff --git a/chromium/components/autofill/core/browser/country_combobox_model.cc b/chromium/components/autofill/core/browser/country_combobox_model.cc new file mode 100644 index 00000000000..c9f8a47a217 --- /dev/null +++ b/chromium/components/autofill/core/browser/country_combobox_model.cc @@ -0,0 +1,99 @@ +// Copyright (c) 2012 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/country_combobox_model.h" + +#include <algorithm> +#include <iterator> +#include <utility> + +#include "base/logging.h" +#include "base/memory/ptr_util.h" +#include "base/strings/utf_string_conversions.h" +#include "components/autofill/core/browser/autofill_country.h" +#include "components/autofill/core/browser/country_data.h" +#include "components/autofill/core/browser/personal_data_manager.h" +#include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_ui.h" +#include "ui/base/l10n/l10n_util_collator.h" +#include "ui/base/models/combobox_model_observer.h" + +namespace autofill { + +CountryComboboxModel::CountryComboboxModel() {} + +CountryComboboxModel::~CountryComboboxModel() {} + +void CountryComboboxModel::SetCountries( + const PersonalDataManager& manager, + const base::Callback<bool(const std::string&)>& filter, + const std::string& app_locale) { + countries_.clear(); + + // Insert the default country at the top as well as in the ordered list. + std::string default_country_code = + manager.GetDefaultCountryCodeForNewAddress(); + DCHECK(!default_country_code.empty()); + + if (filter.is_null() || filter.Run(default_country_code)) { + countries_.push_back( + base::MakeUnique<AutofillCountry>(default_country_code, app_locale)); +#if !defined(OS_ANDROID) + // The separator item. On Android, there are separators after all items, so + // this is unnecessary. + countries_.push_back(nullptr); +#endif + } + + // The sorted list of country codes. + const std::vector<std::string>* available_countries = + &CountryDataMap::GetInstance()->country_codes(); + + // Filter out the countries that do not have rules for address input and + // validation. + const std::vector<std::string>& addressinput_countries = + ::i18n::addressinput::GetRegionCodes(); + std::vector<std::string> filtered_countries; + filtered_countries.reserve(available_countries->size()); + std::set_intersection( + available_countries->begin(), available_countries->end(), + addressinput_countries.begin(), addressinput_countries.end(), + std::back_inserter(filtered_countries)); + available_countries = &filtered_countries; + + CountryVector sorted_countries; + for (const auto& country_code : *available_countries) { + if (filter.is_null() || filter.Run(country_code)) + sorted_countries.push_back( + base::MakeUnique<AutofillCountry>(country_code, app_locale)); + } + + l10n_util::SortStringsUsingMethod(app_locale, &sorted_countries, + &AutofillCountry::name); + std::move(sorted_countries.begin(), sorted_countries.end(), + std::back_inserter(countries_)); +} + +int CountryComboboxModel::GetItemCount() const { + return countries_.size(); +} + +base::string16 CountryComboboxModel::GetItemAt(int index) { + AutofillCountry* country = countries_[index].get(); + if (country) + return countries_[index]->name(); + + // The separator item. Implemented for platforms that don't yet support + // IsItemSeparatorAt(). + return base::ASCIIToUTF16("---"); +} + +bool CountryComboboxModel::IsItemSeparatorAt(int index) { + return !countries_[index].get(); +} + +std::string CountryComboboxModel::GetDefaultCountryCode() const { + return countries_[GetDefaultIndex()]->country_code(); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/country_combobox_model.h b/chromium/components/autofill/core/browser/country_combobox_model.h new file mode 100644 index 00000000000..b3d90c3abf6 --- /dev/null +++ b/chromium/components/autofill/core/browser/country_combobox_model.h @@ -0,0 +1,56 @@ +// Copyright (c) 2012 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_COUNTRY_COMBOBOX_MODEL_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_COUNTRY_COMBOBOX_MODEL_H_ + +#include <memory> +#include <string> +#include <vector> + +#include "base/callback.h" +#include "base/macros.h" +#include "ui/base/models/combobox_model.h" + +namespace autofill { + +class AutofillCountry; +class PersonalDataManager; + +// A model for countries to be used to enter addresses. +class CountryComboboxModel : public ui::ComboboxModel { + public: + using CountryVector = std::vector<std::unique_ptr<AutofillCountry>>; + + CountryComboboxModel(); + ~CountryComboboxModel() override; + + // |filter| is passed each known country's country code. If |filter| returns + // true, an item for that country is added to the model (else it's omitted). + // |manager| determines the default choice. + void SetCountries(const PersonalDataManager& manager, + const base::Callback<bool(const std::string&)>& filter, + const std::string& app_locale); + + // ui::ComboboxModel implementation: + int GetItemCount() const override; + base::string16 GetItemAt(int index) override; + bool IsItemSeparatorAt(int index) override; + + const CountryVector& countries() const { return countries_; } + + // Returns the default country code for this model. + std::string GetDefaultCountryCode() const; + + private: + // The countries to show in the model, including NULL for entries that are + // not countries (the separator entry). + CountryVector countries_; + + DISALLOW_COPY_AND_ASSIGN(CountryComboboxModel); +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_COUNTRY_COMBOBOX_MODEL_H_ diff --git a/chromium/components/autofill/core/browser/country_combobox_model_unittest.cc b/chromium/components/autofill/core/browser/country_combobox_model_unittest.cc new file mode 100644 index 00000000000..ca3e9f0f3be --- /dev/null +++ b/chromium/components/autofill/core/browser/country_combobox_model_unittest.cc @@ -0,0 +1,65 @@ +// Copyright 2014 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/country_combobox_model.h" + +#include <memory> + +#include "components/autofill/core/browser/autofill_country.h" +#include "components/autofill/core/browser/autofill_test_utils.h" +#include "components/autofill/core/browser/test_personal_data_manager.h" +#include "components/prefs/pref_service.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_ui.h" +#include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_ui_component.h" +#include "third_party/libaddressinput/src/cpp/include/libaddressinput/localization.h" + +namespace autofill { + +class CountryComboboxModelTest : public testing::Test { + public: + CountryComboboxModelTest() + : pref_service_(autofill::test::PrefServiceForTesting()) { + manager_.SetTestingPrefService(pref_service_.get()); + manager_.set_timezone_country_code("KR"); + model_.reset(new CountryComboboxModel()); + model_->SetCountries(manager_, base::Callback<bool(const std::string&)>(), + "en-US"); + } + + void TearDown() override { manager_.SetTestingPrefService(nullptr); } + + TestPersonalDataManager* manager() { return &manager_; } + CountryComboboxModel* model() { return model_.get(); } + + private: + TestPersonalDataManager manager_; + std::unique_ptr<PrefService> pref_service_; + std::unique_ptr<CountryComboboxModel> model_; +}; + +TEST_F(CountryComboboxModelTest, DefaultCountryCode) { + std::string default_country = model()->GetDefaultCountryCode(); + EXPECT_EQ(manager()->GetDefaultCountryCodeForNewAddress(), default_country); + + AutofillCountry country(default_country, "en-US"); + EXPECT_EQ(country.name(), model()->GetItemAt(0)); +} + +TEST_F(CountryComboboxModelTest, AllCountriesHaveComponents) { + ::i18n::addressinput::Localization localization; + std::string unused; + for (int i = 0; i < model()->GetItemCount(); ++i) { + if (model()->IsItemSeparatorAt(i)) + continue; + + std::string country_code = model()->countries()[i]->country_code(); + std::vector<::i18n::addressinput::AddressUiComponent> components = + ::i18n::addressinput::BuildComponents(country_code, localization, + std::string(), &unused); + EXPECT_FALSE(components.empty()) << " for country " << country_code; + } +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/country_data.cc b/chromium/components/autofill/core/browser/country_data.cc index 443b2f60b90..90401b09aa4 100644 --- a/chromium/components/autofill/core/browser/country_data.cc +++ b/chromium/components/autofill/core/browser/country_data.cc @@ -7,7 +7,7 @@ #include <utility> #include "base/memory/singleton.h" -#include "grit/components_strings.h" +#include "components/strings/grit/components_strings.h" #include "third_party/icu/source/common/unicode/locid.h" namespace autofill { diff --git a/chromium/components/autofill/core/browser/credit_card.cc b/chromium/components/autofill/core/browser/credit_card.cc index 3b402eae9e8..bd5110eced3 100644 --- a/chromium/components/autofill/core/browser/credit_card.cc +++ b/chromium/components/autofill/core/browser/credit_card.cc @@ -12,6 +12,7 @@ #include <string> #include "base/guid.h" +#include "base/i18n/time_formatting.h" #include "base/logging.h" #include "base/macros.h" #include "base/metrics/histogram_macros.h" @@ -23,14 +24,16 @@ #include "base/time/time.h" #include "build/build_config.h" #include "components/autofill/core/browser/autofill_data_util.h" +#include "components/autofill/core/browser/autofill_experiments.h" #include "components/autofill/core/browser/autofill_field.h" #include "components/autofill/core/browser/autofill_type.h" #include "components/autofill/core/browser/validation.h" +#include "components/autofill/core/common/autofill_clock.h" #include "components/autofill/core/common/autofill_l10n_util.h" #include "components/autofill/core/common/autofill_regexes.h" #include "components/autofill/core/common/form_field_data.h" -#include "grit/components_scaled_resources.h" -#include "grit/components_strings.h" +#include "components/grit/components_scaled_resources.h" +#include "components/strings/grit/components_strings.h" #include "third_party/icu/source/common/unicode/uloc.h" #include "third_party/icu/source/i18n/unicode/dtfmtsym.h" #include "ui/base/l10n/l10n_util.h" @@ -48,6 +51,9 @@ const base::char16 kMidlineEllipsis[] = { 0x0020, 0x0020, namespace { const base::char16 kCreditCardObfuscationSymbol = '*'; +// Time format pattern for short month name (3 digits) and day in month (2 +// digits), e.g. in en-US locale, it can be used to generate "Feb 02". +const char kTimeFormatPatternNoYearShortMonthDate[] = "MMMdd"; bool ConvertYear(const base::string16& year, int* num) { // If the |year| is empty, clear the stored value. @@ -473,7 +479,7 @@ void CreditCard::SetExpirationYear(int expiration_year) { // Will normalize 2-digit years to the 4-digit version. if (expiration_year > 0 && expiration_year < 100) { base::Time::Exploded now_exploded; - base::Time::Now().LocalExplode(&now_exploded); + AutofillClock::Now().LocalExplode(&now_exploded); expiration_year += (now_exploded.year / 100) * 100; } @@ -536,6 +542,55 @@ void CreditCard::operator=(const CreditCard& credit_card) { set_origin(credit_card.origin()); } +base::string16 CreditCard::GetLastUsedDateForDisplay( + const std::string& app_locale) const { + bool show_expiration_date = + ShowExpirationDateInAutofillCreditCardLastUsedDate(); + + DCHECK(use_count() > 0); + // use_count() is initialized as 1 when the card is just added. + if (use_count() == 1) { + return show_expiration_date + ? l10n_util::GetStringFUTF16( + IDS_AUTOFILL_CREDIT_CARD_EXP_AND_ADDED_DATE, + GetInfo(AutofillType(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR), + app_locale), + base::TimeFormatWithPattern( + use_date(), kTimeFormatPatternNoYearShortMonthDate)) + : l10n_util::GetStringFUTF16( + IDS_AUTOFILL_CREDIT_CARD_ADDED_DATE, + base::TimeFormatWithPattern( + use_date(), kTimeFormatPatternNoYearShortMonthDate)); + } + + // use_count() > 1 when the card has been used in autofill. + + // If the card was last used in autofill more than a year ago, + // display "last used over a year ago" without showing date detail. + if ((AutofillClock::Now() - use_date()).InDays() > 365) { + return show_expiration_date + ? l10n_util::GetStringFUTF16( + IDS_AUTOFILL_CREDIT_CARD_EXP_AND_LAST_USED_YEAR_AGO, + GetInfo(AutofillType(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR), + app_locale)) + : l10n_util::GetStringUTF16( + IDS_AUTOFILL_CREDIT_CARD_LAST_USED_YEAR_AGO); + } + + // If the card was last used in autofill within a year, show date information. + return show_expiration_date + ? l10n_util::GetStringFUTF16( + IDS_AUTOFILL_CREDIT_CARD_EXP_AND_LAST_USED_DATE, + GetInfo(AutofillType(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR), + app_locale), + base::TimeFormatWithPattern( + use_date(), kTimeFormatPatternNoYearShortMonthDate)) + : l10n_util::GetStringFUTF16( + IDS_AUTOFILL_CREDIT_CARD_LAST_USED_DATE, + base::TimeFormatWithPattern( + use_date(), kTimeFormatPatternNoYearShortMonthDate)); +} + bool CreditCard::UpdateFromImportedCard(const CreditCard& imported_card, const std::string& app_locale) { if (this->GetInfo(AutofillType(CREDIT_CARD_NUMBER), app_locale) != @@ -550,8 +605,8 @@ bool CreditCard::UpdateFromImportedCard(const CreditCard& imported_card, if (this->IsVerified() && !imported_card.IsVerified()) { // If the original card is expired and the imported card is not, and the // name on the cards are identical, update the expiration date. - if (this->IsExpired(base::Time::Now()) && - !imported_card.IsExpired(base::Time::Now()) && + if (this->IsExpired(AutofillClock::Now()) && + !imported_card.IsExpired(AutofillClock::Now()) && (name_on_card_ == imported_card.name_on_card_)) { DCHECK(imported_card.expiration_month_ && imported_card.expiration_year_); expiration_month_ = imported_card.expiration_month_; @@ -659,52 +714,8 @@ bool CreditCard::IsEmpty(const std::string& app_locale) const { bool CreditCard::IsValid() const { return IsValidCreditCardNumber(number_) && - IsValidCreditCardExpirationDate( - expiration_year_, expiration_month_, base::Time::Now()); -} - -void CreditCard::GetSupportedTypes(ServerFieldTypeSet* supported_types) const { - supported_types->insert(CREDIT_CARD_NAME_FULL); - supported_types->insert(CREDIT_CARD_NAME_FIRST); - supported_types->insert(CREDIT_CARD_NAME_LAST); - supported_types->insert(CREDIT_CARD_NUMBER); - supported_types->insert(CREDIT_CARD_TYPE); - supported_types->insert(CREDIT_CARD_EXP_MONTH); - supported_types->insert(CREDIT_CARD_EXP_2_DIGIT_YEAR); - supported_types->insert(CREDIT_CARD_EXP_4_DIGIT_YEAR); - supported_types->insert(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR); - supported_types->insert(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR); -} - -base::string16 CreditCard::ExpirationMonthAsString() const { - if (expiration_month_ == 0) - return base::string16(); - - base::string16 month = base::IntToString16(expiration_month_); - if (expiration_month_ >= 10) - return month; - - base::string16 zero = ASCIIToUTF16("0"); - zero.append(month); - return zero; -} - -base::string16 CreditCard::TypeForFill() const { - return ::autofill::TypeForFill(type_); -} - -base::string16 CreditCard::Expiration4DigitYearAsString() const { - if (expiration_year_ == 0) - return base::string16(); - - return base::IntToString16(Expiration4DigitYear()); -} - -base::string16 CreditCard::Expiration2DigitYearAsString() const { - if (expiration_year_ == 0) - return base::string16(); - - return base::IntToString16(Expiration2DigitYear()); + IsValidCreditCardExpirationDate(expiration_year_, expiration_month_, + AutofillClock::Now()); } bool CreditCard::SetExpirationMonthFromString(const base::string16& text, @@ -774,6 +785,50 @@ void CreditCard::SetExpirationDateFromString(const base::string16& text) { SetExpirationYear(num); } +void CreditCard::GetSupportedTypes(ServerFieldTypeSet* supported_types) const { + supported_types->insert(CREDIT_CARD_NAME_FULL); + supported_types->insert(CREDIT_CARD_NAME_FIRST); + supported_types->insert(CREDIT_CARD_NAME_LAST); + supported_types->insert(CREDIT_CARD_NUMBER); + supported_types->insert(CREDIT_CARD_TYPE); + supported_types->insert(CREDIT_CARD_EXP_MONTH); + supported_types->insert(CREDIT_CARD_EXP_2_DIGIT_YEAR); + supported_types->insert(CREDIT_CARD_EXP_4_DIGIT_YEAR); + supported_types->insert(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR); + supported_types->insert(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR); +} + +base::string16 CreditCard::ExpirationMonthAsString() const { + if (expiration_month_ == 0) + return base::string16(); + + base::string16 month = base::IntToString16(expiration_month_); + if (expiration_month_ >= 10) + return month; + + base::string16 zero = ASCIIToUTF16("0"); + zero.append(month); + return zero; +} + +base::string16 CreditCard::TypeForFill() const { + return ::autofill::TypeForFill(type_); +} + +base::string16 CreditCard::Expiration4DigitYearAsString() const { + if (expiration_year_ == 0) + return base::string16(); + + return base::IntToString16(Expiration4DigitYear()); +} + +base::string16 CreditCard::Expiration2DigitYearAsString() const { + if (expiration_year_ == 0) + return base::string16(); + + return base::IntToString16(Expiration2DigitYear()); +} + void CreditCard::SetNumber(const base::string16& number) { number_ = number; @@ -785,7 +840,7 @@ void CreditCard::SetNumber(const base::string16& number) { void CreditCard::RecordAndLogUse() { UMA_HISTOGRAM_COUNTS_1000("Autofill.DaysSinceLastUse.CreditCard", - (base::Time::Now() - use_date()).InDays()); + (AutofillClock::Now() - use_date()).InDays()); RecordUse(); } diff --git a/chromium/components/autofill/core/browser/credit_card.h b/chromium/components/autofill/core/browser/credit_card.h index d0a8a6eee7e..0fb251e9deb 100644 --- a/chromium/components/autofill/core/browser/credit_card.h +++ b/chromium/components/autofill/core/browser/credit_card.h @@ -174,6 +174,9 @@ class CreditCard : public AutofillDataModel { // Sets |number_| to |number| and computes the appropriate card |type_|. void SetNumber(const base::string16& number); + // Returns the date when the credit card was last used in autofill. + base::string16 GetLastUsedDateForDisplay(const std::string& app_locale) const; + // Logs the number of days since the credit card was last used and records its // use. void RecordAndLogUse(); @@ -196,6 +199,20 @@ class CreditCard : public AutofillDataModel { billing_address_id_ = id; } + // Sets |expiration_month_| to the integer conversion of |text| and returns + // whether the operation was successful. + bool SetExpirationMonthFromString(const base::string16& text, + const std::string& app_locale); + + // Sets |expiration_year_| to the integer conversion of |text|. Will handle + // 4-digit year or 2-digit year (eventually converted to 4-digit year). + void SetExpirationYearFromString(const base::string16& text); + + // Sets |expiration_year_| and |expiration_month_| to the integer conversion + // of |text|. Will handle mmyy, mmyyyy, mm-yyyy and mm-yy as well as single + // digit months, with various separators. + void SetExpirationDateFromString(const base::string16& text); + private: FRIEND_TEST_ALL_PREFIXES(CreditCardTest, SetExpirationDateFromString); FRIEND_TEST_ALL_PREFIXES(CreditCardTest, SetExpirationYearFromString); @@ -213,20 +230,6 @@ class CreditCard : public AutofillDataModel { base::string16 Expiration4DigitYearAsString() const; base::string16 Expiration2DigitYearAsString() const; - // Sets |expiration_month_| to the integer conversion of |text| and returns - // whether the operation was successful. - bool SetExpirationMonthFromString(const base::string16& text, - const std::string& app_locale); - - // Sets |expiration_year_| to the integer conversion of |text|. Will handle - // 4-digit year or 2-digit year (eventually converted to 4-digit year). - void SetExpirationYearFromString(const base::string16& text); - - // Sets |expiration_year_| and |expiration_month_| to the integer conversion - // of |text|. Will handle mmyy, mmyyyy, mm-yyyy and mm-yy as well as single - // digit months, with various separators. - void SetExpirationDateFromString(const base::string16& text); - // See enum definition above. RecordType record_type_; diff --git a/chromium/components/autofill/core/browser/credit_card_field.cc b/chromium/components/autofill/core/browser/credit_card_field.cc index f53c6865f40..1f23137e4df 100644 --- a/chromium/components/autofill/core/browser/credit_card_field.cc +++ b/chromium/components/autofill/core/browser/credit_card_field.cc @@ -19,8 +19,9 @@ #include "components/autofill/core/browser/autofill_regex_constants.h" #include "components/autofill/core/browser/autofill_scanner.h" #include "components/autofill/core/browser/field_types.h" +#include "components/autofill/core/common/autofill_clock.h" #include "components/autofill/core/common/autofill_regexes.h" -#include "grit/components_strings.h" +#include "components/strings/grit/components_strings.h" #include "ui/base/l10n/l10n_util.h" namespace autofill { @@ -285,7 +286,7 @@ bool CreditCardField::LikelyCardYearSelectField(AutofillScanner* scanner) { if (!MatchesFormControlType(field->form_control_type, MATCH_SELECT)) return false; - const base::Time time_now = base::Time::Now(); + const base::Time time_now = AutofillClock::Now(); base::Time::Exploded time_exploded; time_now.UTCExplode(&time_exploded); diff --git a/chromium/components/autofill/core/browser/credit_card_unittest.cc b/chromium/components/autofill/core/browser/credit_card_unittest.cc index de26d8e56d3..993862f6d6d 100644 --- a/chromium/components/autofill/core/browser/credit_card_unittest.cc +++ b/chromium/components/autofill/core/browser/credit_card_unittest.cc @@ -10,13 +10,15 @@ #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" #include "build/build_config.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" #include "components/autofill/core/browser/credit_card.h" #include "components/autofill/core/browser/validation.h" #include "components/autofill/core/common/autofill_constants.h" #include "components/autofill/core/common/form_field_data.h" -#include "grit/components_scaled_resources.h" +#include "components/grit/components_scaled_resources.h" +#include "components/variations/variations_params_manager.h" #include "testing/gtest/include/gtest/gtest.h" using base::ASCIIToUTF16; @@ -887,4 +889,72 @@ TEST(CreditCardTest, ShouldUpdateExpiration) { } } +// Test that credit card last used date suggestion can be generated correctly +// in different variations. +TEST(CreditCardTest, GetLastUsedDateForDisplay) { + const base::Time::Exploded kTestDateTimeExploded = { + 2016, 12, 6, 10, // Sat, Dec 10, 2016 + 15, 42, 7, 0 // 15:42:07.000 + }; + base::Time kArbitraryTime; + EXPECT_TRUE( + base::Time::FromLocalExploded(kTestDateTimeExploded, &kArbitraryTime)); + + // Test for added to chrome/chromium. + CreditCard credit_card0(base::GenerateGUID(), "https://www.example.com"); + credit_card0.set_use_count(1); + credit_card0.set_use_date(kArbitraryTime - base::TimeDelta::FromDays(1)); + test::SetCreditCardInfo(&credit_card0, "John Dillinger", + "423456789012" /* Visa */, "01", "2021"); + + // Test for last used date. + CreditCard credit_card1(base::GenerateGUID(), "https://www.example.com"); + test::SetCreditCardInfo(&credit_card1, "Clyde Barrow", + "347666888555" /* American Express */, "04", "2021"); + credit_card1.set_use_count(10); + credit_card1.set_use_date(kArbitraryTime - base::TimeDelta::FromDays(10)); + + // Test for last used more than one year ago. + CreditCard credit_card2(base::GenerateGUID(), "https://www.example.com"); + credit_card2.set_use_count(5); + credit_card2.set_use_date(kArbitraryTime - base::TimeDelta::FromDays(366)); + test::SetCreditCardInfo(&credit_card2, "Bonnie Parker", + "518765432109" /* Mastercard */, "12", "2021"); + + static const struct { + const char* show_expiration_date; + const std::string& app_locale; + base::string16 added_to_autofill_date; + base::string16 last_used_date; + base::string16 last_used_year_ago; + } kTestCases[] = { + // only show last used date. + {"false", "en_US", ASCIIToUTF16("Added Dec 09"), + ASCIIToUTF16("Last used Nov 30"), + ASCIIToUTF16("Last used over a year ago")}, + // show expiration date and last used date. + {"true", "en_US", ASCIIToUTF16("Exp: 01/21, added Dec 09"), + ASCIIToUTF16("Exp: 04/21, last used Nov 30"), + ASCIIToUTF16("Exp: 12/21, last used over a year ago")}, + }; + + variations::testing::VariationParamsManager variation_params_; + + for (const auto& test_case : kTestCases) { + variation_params_.SetVariationParamsWithFeatureAssociations( + kAutofillCreditCardLastUsedDateDisplay.name, + {{kAutofillCreditCardLastUsedDateShowExpirationDateKey, + test_case.show_expiration_date}}, + {kAutofillCreditCardLastUsedDateDisplay.name}); + + EXPECT_EQ(test_case.added_to_autofill_date, + credit_card0.GetLastUsedDateForDisplay(test_case.app_locale)); + EXPECT_EQ(test_case.last_used_date, + credit_card1.GetLastUsedDateForDisplay(test_case.app_locale)); + EXPECT_EQ(test_case.last_used_year_ago, + credit_card2.GetLastUsedDateForDisplay(test_case.app_locale)); + variation_params_.ClearAllVariationParams(); + } +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/form_structure.cc b/chromium/components/autofill/core/browser/form_structure.cc index 278cc7b6de3..addf89f686b 100644 --- a/chromium/components/autofill/core/browser/form_structure.cc +++ b/chromium/components/autofill/core/browser/form_structure.cc @@ -628,7 +628,7 @@ 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 auto& field = cached_form.field(i); + auto* const field = cached_form.field(i); cached_fields[field->FieldSignatureAsStr()] = field; } @@ -681,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 auto& field = this->field(i); + auto* const 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 @@ -1159,6 +1159,9 @@ void FormStructure::EncodeFormForUpload(AutofillUploadContents* upload) const { if (!field->name.empty()) added_field->set_name(base::UTF16ToUTF8(field->name)); + if (!field->id.empty()) + added_field->set_id(base::UTF16ToUTF8(field->id)); + if (!field->autocomplete_attribute.empty()) added_field->set_autocomplete(field->autocomplete_attribute); diff --git a/chromium/components/autofill/core/browser/form_structure_unittest.cc b/chromium/components/autofill/core/browser/form_structure_unittest.cc index aaa39253621..c1ecb59405c 100644 --- a/chromium/components/autofill/core/browser/form_structure_unittest.cc +++ b/chromium/components/autofill/core/browser/form_structure_unittest.cc @@ -2191,15 +2191,15 @@ TEST_F(FormStructureTest, EncodeUploadRequest) { upload.set_action_signature(15724779818122431245U); test::FillUploadField(upload.add_field(), 3763331450U, "firstname", "text", - nullptr, 3U, nullptr); + nullptr, 3U); test::FillUploadField(upload.add_field(), 3494530716U, "lastname", "text", - nullptr, 5U, nullptr); + nullptr, 5U); test::FillUploadField(upload.add_field(), 1029417091U, "email", "email", - nullptr, 9U, nullptr); + nullptr, 9U); test::FillUploadField(upload.add_field(), 466116101U, "phone", "number", - nullptr, 14U, nullptr); + nullptr, 14U); test::FillUploadField(upload.add_field(), 2799270304U, "country", - "select-one", nullptr, 36U, nullptr); + "select-one", nullptr, 36U); std::string expected_upload_string; ASSERT_TRUE(upload.SerializeToString(&expected_upload_string)); @@ -2247,7 +2247,7 @@ TEST_F(FormStructureTest, EncodeUploadRequest) { // Create an additonal 8 fields (total of 13). for (int i = 0; i < 8; ++i) { test::FillUploadField(upload.add_field(), 509334676U, "address", "text", - nullptr, 30U, nullptr); + nullptr, 30U); } // Put the appropriate autofill type on the different address fields. upload.mutable_field(6)->set_autofill_type(31U); @@ -2375,14 +2375,14 @@ TEST_F(FormStructureTest, AutofillUploadContents::Field* upload_firstname_field = upload.add_field(); test::FillUploadField(upload_firstname_field, 4224610201U, "firstname", "", - "given-name", 3U, nullptr); + "given-name", 3U); upload_firstname_field->set_form_classifier_outcome( AutofillUploadContents::Field::NON_GENERATION_ELEMENT); upload_firstname_field->set_properties_mask(FieldPropertiesFlags::HAD_FOCUS); AutofillUploadContents::Field* upload_lastname_field = upload.add_field(); test::FillUploadField(upload_lastname_field, 2786066110U, "lastname", "", - "family-name", 5U, nullptr); + "family-name", 5U); upload_lastname_field->set_form_classifier_outcome( AutofillUploadContents::Field::NON_GENERATION_ELEMENT); upload_lastname_field->set_properties_mask(FieldPropertiesFlags::HAD_FOCUS | @@ -2390,7 +2390,7 @@ TEST_F(FormStructureTest, AutofillUploadContents::Field* upload_email_field = upload.add_field(); test::FillUploadField(upload_email_field, 1029417091U, "email", "email", - "email", 9U, nullptr); + "email", 9U); upload_email_field->set_form_classifier_outcome( AutofillUploadContents::Field::NON_GENERATION_ELEMENT); upload_email_field->set_properties_mask(FieldPropertiesFlags::HAD_FOCUS | @@ -2398,7 +2398,7 @@ TEST_F(FormStructureTest, AutofillUploadContents::Field* upload_username_field = upload.add_field(); test::FillUploadField(upload_username_field, 239111655U, "username", "text", - "email", 86U, nullptr); + "email", 86U); upload_username_field->set_form_classifier_outcome( AutofillUploadContents::Field::NON_GENERATION_ELEMENT); upload_username_field->set_properties_mask(FieldPropertiesFlags::HAD_FOCUS | @@ -2406,7 +2406,7 @@ TEST_F(FormStructureTest, AutofillUploadContents::Field* upload_password_field = upload.add_field(); test::FillUploadField(upload_password_field, 2051817934U, "password", - "password", "email", 76U, nullptr); + "password", "email", 76U); upload_password_field->set_form_classifier_outcome( AutofillUploadContents::Field::GENERATION_ELEMENT); upload_password_field->set_generation_type( @@ -2480,11 +2480,11 @@ TEST_F(FormStructureTest, EncodeUploadRequest_WithAutocomplete) { upload.set_action_signature(15724779818122431245U); test::FillUploadField(upload.add_field(), 3763331450U, "firstname", "text", - "given-name", 3U, nullptr); + "given-name", 3U); test::FillUploadField(upload.add_field(), 3494530716U, "lastname", "text", - "family-name", 5U, nullptr); + "family-name", 5U); test::FillUploadField(upload.add_field(), 1029417091U, "email", "email", - "email", 9U, nullptr); + "email", 9U); std::string expected_upload_string; ASSERT_TRUE(upload.SerializeToString(&expected_upload_string)); @@ -2548,11 +2548,11 @@ TEST_F(FormStructureTest, EncodeUploadRequest_ObservedSubmissionFalse) { upload.set_action_signature(15724779818122431245U); test::FillUploadField(upload.add_field(), 3763331450U, "firstname", "text", - nullptr, 3U, nullptr); + nullptr, 3U); test::FillUploadField(upload.add_field(), 3494530716U, "lastname", "text", - nullptr, 5U, nullptr); + nullptr, 5U); test::FillUploadField(upload.add_field(), 1029417091U, "email", "email", - nullptr, 9U, nullptr); + nullptr, 9U); std::string expected_upload_string; ASSERT_TRUE(upload.SerializeToString(&expected_upload_string)); @@ -2613,11 +2613,11 @@ TEST_F(FormStructureTest, EncodeUploadRequest_WithLabels) { upload.set_action_signature(15724779818122431245U); test::FillUploadField(upload.add_field(), 1318412689U, nullptr, "text", - nullptr, 3U, nullptr); + nullptr, 3U); test::FillUploadField(upload.add_field(), 1318412689U, nullptr, "text", - nullptr, 5U, nullptr); + nullptr, 5U); test::FillUploadField(upload.add_field(), 1318412689U, nullptr, "text", - nullptr, 9U, nullptr); + nullptr, 9U); std::string expected_upload_string; ASSERT_TRUE(upload.SerializeToString(&expected_upload_string)); @@ -2631,7 +2631,7 @@ TEST_F(FormStructureTest, EncodeUploadRequest_WithLabels) { EXPECT_EQ(expected_upload_string, encoded_upload_string); } -TEST_F(FormStructureTest, EncodeUploadRequest_WithCssClasses) { +TEST_F(FormStructureTest, EncodeUploadRequest_WithCssClassesAndIds) { std::vector<ServerFieldTypeSet> possible_field_types; FormData form; @@ -2643,11 +2643,13 @@ TEST_F(FormStructureTest, EncodeUploadRequest_WithCssClasses) { possible_field_types.back().insert(NAME_FIRST); field.css_classes = ASCIIToUTF16("last_name_field"); + field.id = ASCIIToUTF16("lastname_id"); form.fields.push_back(field); possible_field_types.push_back(ServerFieldTypeSet()); possible_field_types.back().insert(NAME_LAST); field.css_classes = ASCIIToUTF16("email_field required_field"); + field.id = ASCIIToUTF16("email_id"); form.fields.push_back(field); possible_field_types.push_back(ServerFieldTypeSet()); possible_field_types.back().insert(EMAIL_ADDRESS); @@ -2672,12 +2674,20 @@ TEST_F(FormStructureTest, EncodeUploadRequest_WithCssClasses) { upload.set_data_present("1440"); upload.set_action_signature(15724779818122431245U); - test::FillUploadField(upload.add_field(), 1318412689U, nullptr, "text", - nullptr, 3U, nullptr); - test::FillUploadField(upload.add_field(), 1318412689U, nullptr, "text", - nullptr, 5U, "last_name_field"); - test::FillUploadField(upload.add_field(), 1318412689U, nullptr, "text", - nullptr, 9U, "email_field required_field"); + AutofillUploadContents::Field* firstname_field = upload.add_field(); + test::FillUploadField(firstname_field, 1318412689U, nullptr, "text", nullptr, + 3U); + + AutofillUploadContents::Field* lastname_field = upload.add_field(); + test::FillUploadField(lastname_field, 1318412689U, nullptr, "text", nullptr, + 5U); + lastname_field->set_id("lastname_id"); + lastname_field->set_css_classes("last_name_field"); + + AutofillUploadContents::Field* email_field = upload.add_field(); + test::FillUploadField(email_field, 1318412689U, nullptr, "text", nullptr, 9U); + email_field->set_id("email_id"); + email_field->set_css_classes("email_field required_field"); std::string expected_upload_string; ASSERT_TRUE(upload.SerializeToString(&expected_upload_string)); @@ -2738,11 +2748,11 @@ TEST_F(FormStructureTest, EncodeUploadRequest_WithFormName) { upload.set_form_name("myform"); test::FillUploadField(upload.add_field(), 1318412689U, nullptr, "text", - nullptr, 3U, nullptr); + nullptr, 3U); test::FillUploadField(upload.add_field(), 1318412689U, nullptr, "text", - nullptr, 5U, nullptr); + nullptr, 5U); test::FillUploadField(upload.add_field(), 1318412689U, nullptr, "text", - nullptr, 9U, nullptr); + nullptr, 9U); std::string expected_upload_string; ASSERT_TRUE(upload.SerializeToString(&expected_upload_string)); @@ -2808,11 +2818,11 @@ TEST_F(FormStructureTest, EncodeUploadRequestPartialMetadata) { upload.set_action_signature(15724779818122431245U); test::FillUploadField(upload.add_field(), 1318412689U, nullptr, "text", - nullptr, 3U, nullptr); + nullptr, 3U); test::FillUploadField(upload.add_field(), 3494530716U, "lastname", "text", - "family-name", 5U, nullptr); + "family-name", 5U); test::FillUploadField(upload.add_field(), 1545468175U, "lastname", "email", - "email", 9U, nullptr); + "email", 9U); std::string expected_upload_string; ASSERT_TRUE(upload.SerializeToString(&expected_upload_string)); @@ -2841,6 +2851,7 @@ TEST_F(FormStructureTest, EncodeUploadRequest_DisabledMetadataTrial) { field.label = ASCIIToUTF16("First Name"); field.name = ASCIIToUTF16("firstname"); + field.id = ASCIIToUTF16("first_name"); field.autocomplete_attribute = "given-name"; field.css_classes = ASCIIToUTF16("class1 class2"); form.fields.push_back(field); @@ -2849,6 +2860,7 @@ TEST_F(FormStructureTest, EncodeUploadRequest_DisabledMetadataTrial) { field.label = ASCIIToUTF16("Last Name"); field.name = ASCIIToUTF16("lastname"); + field.id = ASCIIToUTF16("last_name"); field.autocomplete_attribute = "family-name"; field.css_classes = ASCIIToUTF16("class1 class2"); form.fields.push_back(field); @@ -2857,6 +2869,7 @@ TEST_F(FormStructureTest, EncodeUploadRequest_DisabledMetadataTrial) { field.label = ASCIIToUTF16("Email"); field.name = ASCIIToUTF16("email"); + field.id = ASCIIToUTF16("e-mail"); field.form_control_type = "email"; field.autocomplete_attribute = "email"; field.css_classes = ASCIIToUTF16("class1 class2"); @@ -2884,11 +2897,11 @@ TEST_F(FormStructureTest, EncodeUploadRequest_DisabledMetadataTrial) { upload.set_data_present("1440"); test::FillUploadField(upload.add_field(), 3763331450U, nullptr, nullptr, - nullptr, 3U, nullptr); + nullptr, 3U); test::FillUploadField(upload.add_field(), 3494530716U, nullptr, nullptr, - nullptr, 5U, nullptr); + nullptr, 5U); test::FillUploadField(upload.add_field(), 1029417091U, nullptr, nullptr, - nullptr, 9U, nullptr); + nullptr, 9U); std::string expected_upload_string; ASSERT_TRUE(upload.SerializeToString(&expected_upload_string)); @@ -2944,11 +2957,11 @@ TEST_F(FormStructureTest, CheckDataPresence) { upload.set_action_signature(15724779818122431245U); test::FillUploadField(upload.add_field(), 1089846351U, "first", "text", - nullptr, 1U, nullptr); + nullptr, 1U); test::FillUploadField(upload.add_field(), 2404144663U, "last", "text", - nullptr, 1U, nullptr); + nullptr, 1U); test::FillUploadField(upload.add_field(), 420638584U, "email", "text", - nullptr, 1U, nullptr); + nullptr, 1U); std::string expected_upload_string; ASSERT_TRUE(upload.SerializeToString(&expected_upload_string)); @@ -3212,13 +3225,13 @@ TEST_F(FormStructureTest, CheckMultipleTypes) { upload.set_action_signature(15724779818122431245U); test::FillUploadField(upload.add_field(), 420638584U, "email", "text", - nullptr, 9U, nullptr); + nullptr, 9U); test::FillUploadField(upload.add_field(), 1089846351U, "first", "text", - nullptr, 3U, nullptr); + nullptr, 3U); test::FillUploadField(upload.add_field(), 2404144663U, "last", "text", - nullptr, 5U, nullptr); + nullptr, 5U); test::FillUploadField(upload.add_field(), 509334676U, "address", "text", - nullptr, 30U, nullptr); + nullptr, 30U); std::string expected_upload_string; ASSERT_TRUE(upload.SerializeToString(&expected_upload_string)); @@ -3240,10 +3253,10 @@ TEST_F(FormStructureTest, CheckMultipleTypes) { upload.mutable_field(2)->set_autofill_type(3); // Replace the fourth field by the old third field. test::FillUploadField(upload.mutable_field(3), 2404144663U, "last", "text", - nullptr, 5U, nullptr); + nullptr, 5U); // Re-add the old fourth field. test::FillUploadField(upload.add_field(), 509334676U, "address", "text", - nullptr, 30U, nullptr); + nullptr, 30U); ASSERT_TRUE(upload.SerializeToString(&expected_upload_string)); @@ -3261,7 +3274,7 @@ TEST_F(FormStructureTest, CheckMultipleTypes) { // Adjust the expected upload proto. test::FillUploadField(upload.add_field(), 509334676U, "address", "text", - nullptr, 31U, nullptr); + nullptr, 31U); ASSERT_TRUE(upload.SerializeToString(&expected_upload_string)); AutofillUploadContents encoded_upload3; diff --git a/chromium/components/autofill/core/browser/payments/full_card_request.cc b/chromium/components/autofill/core/browser/payments/full_card_request.cc index f2f63805e58..ee21ebaf829 100644 --- a/chromium/components/autofill/core/browser/payments/full_card_request.cc +++ b/chromium/components/autofill/core/browser/payments/full_card_request.cc @@ -8,10 +8,10 @@ #include "base/logging.h" #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" -#include "components/autofill/core/browser/autofill_client.h" #include "components/autofill/core/browser/autofill_metrics.h" #include "components/autofill/core/browser/credit_card.h" #include "components/autofill/core/browser/personal_data_manager.h" +#include "components/autofill/core/common/autofill_clock.h" namespace autofill { namespace payments { @@ -22,7 +22,8 @@ FullCardRequest::FullCardRequest(AutofillClient* autofill_client, : autofill_client_(autofill_client), payments_client_(payments_client), personal_data_manager_(personal_data_manager), - delegate_(nullptr), + result_delegate_(nullptr), + ui_delegate_(nullptr), should_unmask_card_(false), weak_ptr_factory_(this) { DCHECK(autofill_client_); @@ -34,28 +35,31 @@ FullCardRequest::~FullCardRequest() {} void FullCardRequest::GetFullCard(const CreditCard& card, AutofillClient::UnmaskCardReason reason, - base::WeakPtr<Delegate> delegate) { - DCHECK(delegate); - - // Only request can be active a time. If the member variable |delegate_| is - // already set, then immediately reject the new request through the method - // parameter |delegate|. - if (delegate_) { - delegate->OnFullCardRequestFailed(); + base::WeakPtr<ResultDelegate> result_delegate, + base::WeakPtr<UIDelegate> ui_delegate) { + DCHECK(result_delegate); + DCHECK(ui_delegate); + + // Only one request can be active at a time. If the member variable + // |result_delegate_| is already set, then immediately reject the new request + // through the method parameter |result_delegate_|. + if (result_delegate_) { + result_delegate_->OnFullCardRequestFailed(); return; } - delegate_ = delegate; + result_delegate_ = result_delegate; + ui_delegate_ = ui_delegate; request_.reset(new payments::PaymentsClient::UnmaskRequestDetails); request_->card = card; should_unmask_card_ = card.record_type() == CreditCard::MASKED_SERVER_CARD || (card.record_type() == CreditCard::FULL_SERVER_CARD && - card.ShouldUpdateExpiration(base::Time::Now())); + card.ShouldUpdateExpiration(AutofillClock::Now())); if (should_unmask_card_) payments_client_->Prepare(); - autofill_client_->ShowUnmaskPrompt(request_->card, reason, - weak_ptr_factory_.GetWeakPtr()); + ui_delegate_->ShowUnmaskPrompt(request_->card, reason, + weak_ptr_factory_.GetWeakPtr()); if (should_unmask_card_) { autofill_client_->LoadRiskData( @@ -82,23 +86,26 @@ void FullCardRequest::OnUnmaskResponse(const UnmaskResponse& response) { } if (!should_unmask_card_) { - if (delegate_) - delegate_->OnFullCardRequestSucceeded(request_->card, response.cvc); + if (result_delegate_) + result_delegate_->OnFullCardRequestSucceeded(request_->card, + response.cvc); + if (ui_delegate_) + ui_delegate_->OnUnmaskVerificationResult(AutofillClient::SUCCESS); Reset(); - autofill_client_->OnUnmaskVerificationResult(AutofillClient::SUCCESS); + return; } request_->user_response = response; if (!request_->risk_data.empty()) { - real_pan_request_timestamp_ = base::Time::Now(); + real_pan_request_timestamp_ = AutofillClock::Now(); payments_client_->UnmaskCard(*request_); } } void FullCardRequest::OnUnmaskPromptClosed() { - if (delegate_) - delegate_->OnFullCardRequestFailed(); + if (result_delegate_) + result_delegate_->OnFullCardRequestFailed(); Reset(); } @@ -106,7 +113,7 @@ void FullCardRequest::OnUnmaskPromptClosed() { void FullCardRequest::OnDidGetUnmaskRiskData(const std::string& risk_data) { request_->risk_data = risk_data; if (!request_->user_response.cvc.empty()) { - real_pan_request_timestamp_ = base::Time::Now(); + real_pan_request_timestamp_ = AutofillClock::Now(); payments_client_->UnmaskCard(*request_); } } @@ -114,7 +121,10 @@ void FullCardRequest::OnDidGetUnmaskRiskData(const std::string& risk_data) { void FullCardRequest::OnDidGetRealPan(AutofillClient::PaymentsRpcResult result, const std::string& real_pan) { AutofillMetrics::LogRealPanDuration( - base::Time::Now() - real_pan_request_timestamp_, result); + AutofillClock::Now() - real_pan_request_timestamp_, result); + + if (ui_delegate_) + ui_delegate_->OnUnmaskVerificationResult(result); switch (result) { // Wait for user retry. @@ -125,8 +135,8 @@ void FullCardRequest::OnDidGetRealPan(AutofillClient::PaymentsRpcResult result, case AutofillClient::PERMANENT_FAILURE: // Intentional fall through. case AutofillClient::NETWORK_ERROR: { - if (delegate_) - delegate_->OnFullCardRequestFailed(); + if (result_delegate_) + result_delegate_->OnFullCardRequestFailed(); Reset(); break; } @@ -140,9 +150,9 @@ void FullCardRequest::OnDidGetRealPan(AutofillClient::PaymentsRpcResult result, if (request_->user_response.should_store_pan) personal_data_manager_->UpdateServerCreditCard(request_->card); - if (delegate_) - delegate_->OnFullCardRequestSucceeded(request_->card, - request_->user_response.cvc); + if (result_delegate_) + result_delegate_->OnFullCardRequestSucceeded( + request_->card, request_->user_response.cvc); Reset(); break; } @@ -151,14 +161,13 @@ void FullCardRequest::OnDidGetRealPan(AutofillClient::PaymentsRpcResult result, NOTREACHED(); break; } - - autofill_client_->OnUnmaskVerificationResult(result); } void FullCardRequest::Reset() { weak_ptr_factory_.InvalidateWeakPtrs(); payments_client_->CancelRequest(); - delegate_ = nullptr; + result_delegate_ = nullptr; + ui_delegate_ = nullptr; request_.reset(); should_unmask_card_ = false; } diff --git a/chromium/components/autofill/core/browser/payments/full_card_request.h b/chromium/components/autofill/core/browser/payments/full_card_request.h index 6fd06ae5250..263b3c88fe7 100644 --- a/chromium/components/autofill/core/browser/payments/full_card_request.h +++ b/chromium/components/autofill/core/browser/payments/full_card_request.h @@ -12,12 +12,12 @@ #include "base/memory/weak_ptr.h" #include "base/strings/string16.h" #include "base/time/time.h" +#include "components/autofill/core/browser/autofill_client.h" #include "components/autofill/core/browser/card_unmask_delegate.h" #include "components/autofill/core/browser/payments/payments_client.h" namespace autofill { -class AutofillClient; class CreditCard; class PersonalDataManager; @@ -27,13 +27,26 @@ namespace payments { class FullCardRequest : public CardUnmaskDelegate { public: // The interface for receiving the full card details. - class Delegate { + class ResultDelegate { public: + virtual ~ResultDelegate() = default; virtual void OnFullCardRequestSucceeded(const CreditCard& card, const base::string16& cvc) = 0; virtual void OnFullCardRequestFailed() = 0; }; + // The delegate responsible for displaying the unmask prompt UI. + class UIDelegate { + public: + virtual ~UIDelegate() = default; + virtual void ShowUnmaskPrompt( + const CreditCard& card, + AutofillClient::UnmaskCardReason reason, + base::WeakPtr<CardUnmaskDelegate> delegate) = 0; + virtual void OnUnmaskVerificationResult( + AutofillClient::PaymentsRpcResult result) = 0; + }; + // The parameters should outlive the FullCardRequest. FullCardRequest(AutofillClient* autofill_client, payments::PaymentsClient* payments_client, @@ -50,7 +63,8 @@ class FullCardRequest : public CardUnmaskDelegate { // autofill table on disk. void GetFullCard(const CreditCard& card, AutofillClient::UnmaskCardReason reason, - base::WeakPtr<Delegate> delegate); + base::WeakPtr<ResultDelegate> result_delegate, + base::WeakPtr<UIDelegate> ui_delegate); // Returns true if there's a pending request to get the full card. bool IsGettingFullCard() const; @@ -80,7 +94,10 @@ class FullCardRequest : public CardUnmaskDelegate { PersonalDataManager* const personal_data_manager_; // Receiver of the full PAN and CVC. - base::WeakPtr<Delegate> delegate_; + base::WeakPtr<ResultDelegate> result_delegate_; + + // Delegate responsible for displaying the unmask prompt UI. + base::WeakPtr<UIDelegate> ui_delegate_; // The pending request to get a card's full PAN and CVC. std::unique_ptr<payments::PaymentsClient::UnmaskRequestDetails> request_; 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 480f71c8aa4..76e3637540b 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 @@ -27,14 +27,26 @@ namespace payments { using testing::_; // The consumer of the full card request API. -class MockDelegate : public FullCardRequest::Delegate, - public base::SupportsWeakPtr<MockDelegate> { +class MockResultDelegate : public FullCardRequest::ResultDelegate, + public base::SupportsWeakPtr<MockResultDelegate> { public: MOCK_METHOD2(OnFullCardRequestSucceeded, void(const CreditCard&, const base::string16&)); MOCK_METHOD0(OnFullCardRequestFailed, void()); }; +// The delegate responsible for displaying the unmask prompt UI. +class MockUIDelegate : public FullCardRequest::UIDelegate, + public base::SupportsWeakPtr<MockUIDelegate> { + public: + MOCK_METHOD3(ShowUnmaskPrompt, + void(const CreditCard&, + AutofillClient::UnmaskCardReason, + base::WeakPtr<CardUnmaskDelegate>)); + MOCK_METHOD1(OnUnmaskVerificationResult, + void(AutofillClient::PaymentsRpcResult)); +}; + // The personal data manager. class MockPersonalDataManager : public PersonalDataManager { public: @@ -44,17 +56,6 @@ class MockPersonalDataManager : public PersonalDataManager { MOCK_METHOD1(UpdateServerCreditCard, void(const CreditCard& credit_card)); }; -// The autofill client. -class MockAutofillClient : public TestAutofillClient { - public: - MOCK_METHOD3(ShowUnmaskPrompt, - void(const CreditCard&, - UnmaskCardReason, - base::WeakPtr<CardUnmaskDelegate>)); - MOCK_METHOD1(OnUnmaskVerificationResult, - void(AutofillClient::PaymentsRpcResult)); -}; - // The test fixture for full card request. class FullCardRequestTest : public testing::Test, public PaymentsClientDelegate { @@ -74,15 +75,15 @@ class FullCardRequestTest : public testing::Test, MockPersonalDataManager* personal_data() { return &personal_data_; } - MockAutofillClient* client() { return &autofill_client_; } - FullCardRequest* request() { return &request_; } - CardUnmaskDelegate* ui_delegate() { + CardUnmaskDelegate* card_unmask_delegate() { return static_cast<CardUnmaskDelegate*>(&request_); } - MockDelegate* delegate() { return &delegate_; } + MockResultDelegate* result_delegate() { return &result_delegate_; } + + MockUIDelegate* ui_delegate() { return &ui_delegate_; } // PaymentsClientDelegate: void OnDidGetRealPan(AutofillClient::PaymentsRpcResult result, @@ -107,10 +108,11 @@ class FullCardRequestTest : public testing::Test, base::MessageLoop message_loop_; MockPersonalDataManager personal_data_; - MockAutofillClient autofill_client_; + TestAutofillClient autofill_client_; scoped_refptr<net::TestURLRequestContextGetter> request_context_; PaymentsClient payments_client_; - MockDelegate delegate_; + MockResultDelegate result_delegate_; + MockUIDelegate ui_delegate_; FullCardRequest request_; DISALLOW_COPY_AND_ASSIGN(FullCardRequestTest); @@ -134,96 +136,106 @@ MATCHER_P4(CardMatches, record_type, number, month, year, "") { // Verify getting the full PAN and the CVC for a masked server card. TEST_F(FullCardRequestTest, GetFullCardPanAndCvcForMaskedServerCard) { - EXPECT_CALL(*delegate(), + EXPECT_CALL(*result_delegate(), OnFullCardRequestSucceeded( CardMatches(CreditCard::FULL_SERVER_CARD, "4111"), base::ASCIIToUTF16("123"))); - EXPECT_CALL(*client(), ShowUnmaskPrompt(_, _, _)); - EXPECT_CALL(*client(), OnUnmaskVerificationResult(AutofillClient::SUCCESS)); + EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _)); + EXPECT_CALL(*ui_delegate(), + OnUnmaskVerificationResult(AutofillClient::SUCCESS)); request()->GetFullCard( CreditCard(CreditCard::MASKED_SERVER_CARD, "server_id"), - AutofillClient::UNMASK_FOR_AUTOFILL, delegate()->AsWeakPtr()); + AutofillClient::UNMASK_FOR_AUTOFILL, result_delegate()->AsWeakPtr(), + ui_delegate()->AsWeakPtr()); CardUnmaskDelegate::UnmaskResponse response; response.cvc = base::ASCIIToUTF16("123"); - ui_delegate()->OnUnmaskResponse(response); + card_unmask_delegate()->OnUnmaskResponse(response); OnDidGetRealPan(AutofillClient::SUCCESS, "4111"); - ui_delegate()->OnUnmaskPromptClosed(); + card_unmask_delegate()->OnUnmaskPromptClosed(); } // Verify getting the CVC for a local card. TEST_F(FullCardRequestTest, GetFullCardPanAndCvcForLocalCard) { - EXPECT_CALL(*delegate(), OnFullCardRequestSucceeded( - CardMatches(CreditCard::LOCAL_CARD, "4111"), - base::ASCIIToUTF16("123"))); - EXPECT_CALL(*client(), ShowUnmaskPrompt(_, _, _)); - EXPECT_CALL(*client(), OnUnmaskVerificationResult(AutofillClient::SUCCESS)); + EXPECT_CALL( + *result_delegate(), + OnFullCardRequestSucceeded(CardMatches(CreditCard::LOCAL_CARD, "4111"), + base::ASCIIToUTF16("123"))); + EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _)); + EXPECT_CALL(*ui_delegate(), + OnUnmaskVerificationResult(AutofillClient::SUCCESS)); CreditCard card; test::SetCreditCardInfo(&card, nullptr, "4111", "12", "2050"); request()->GetFullCard(card, AutofillClient::UNMASK_FOR_AUTOFILL, - delegate()->AsWeakPtr()); + result_delegate()->AsWeakPtr(), + ui_delegate()->AsWeakPtr()); CardUnmaskDelegate::UnmaskResponse response; response.cvc = base::ASCIIToUTF16("123"); - ui_delegate()->OnUnmaskResponse(response); - ui_delegate()->OnUnmaskPromptClosed(); + card_unmask_delegate()->OnUnmaskResponse(response); + card_unmask_delegate()->OnUnmaskPromptClosed(); } // Verify getting the CVC for an unmasked server card. TEST_F(FullCardRequestTest, GetFullCardPanAndCvcForFullServerCard) { - EXPECT_CALL(*delegate(), + EXPECT_CALL(*result_delegate(), OnFullCardRequestSucceeded( CardMatches(CreditCard::FULL_SERVER_CARD, "4111"), base::ASCIIToUTF16("123"))); - EXPECT_CALL(*client(), ShowUnmaskPrompt(_, _, _)); - EXPECT_CALL(*client(), OnUnmaskVerificationResult(AutofillClient::SUCCESS)); + EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _)); + EXPECT_CALL(*ui_delegate(), + OnUnmaskVerificationResult(AutofillClient::SUCCESS)); CreditCard full_server_card(CreditCard::FULL_SERVER_CARD, "server_id"); test::SetCreditCardInfo(&full_server_card, nullptr, "4111", "12", "2050"); request()->GetFullCard(full_server_card, AutofillClient::UNMASK_FOR_AUTOFILL, - delegate()->AsWeakPtr()); + result_delegate()->AsWeakPtr(), + ui_delegate()->AsWeakPtr()); CardUnmaskDelegate::UnmaskResponse response; response.cvc = base::ASCIIToUTF16("123"); - ui_delegate()->OnUnmaskResponse(response); - ui_delegate()->OnUnmaskPromptClosed(); + card_unmask_delegate()->OnUnmaskResponse(response); + card_unmask_delegate()->OnUnmaskPromptClosed(); } // Verify getting the CVC for an unmasked server card with EXPIRED server // status. TEST_F(FullCardRequestTest, GetFullCardPanAndCvcForFullServerCardInExpiredStatus) { - EXPECT_CALL(*delegate(), OnFullCardRequestSucceeded( - CardMatches(CreditCard::FULL_SERVER_CARD, "4111", - "12", "2051"), - base::ASCIIToUTF16("123"))); - EXPECT_CALL(*client(), ShowUnmaskPrompt(_, _, _)); + EXPECT_CALL(*result_delegate(), OnFullCardRequestSucceeded( + CardMatches(CreditCard::FULL_SERVER_CARD, + "4111", "12", "2051"), + base::ASCIIToUTF16("123"))); + EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _)); EXPECT_CALL(*personal_data(), UpdateServerCreditCard(_)).Times(0); - EXPECT_CALL(*client(), OnUnmaskVerificationResult(AutofillClient::SUCCESS)); + EXPECT_CALL(*ui_delegate(), + OnUnmaskVerificationResult(AutofillClient::SUCCESS)); CreditCard full_server_card(CreditCard::FULL_SERVER_CARD, "server_id"); test::SetCreditCardInfo(&full_server_card, nullptr, "4111", "12", "2050"); full_server_card.SetServerStatus(CreditCard::EXPIRED); request()->GetFullCard(full_server_card, AutofillClient::UNMASK_FOR_AUTOFILL, - delegate()->AsWeakPtr()); + result_delegate()->AsWeakPtr(), + ui_delegate()->AsWeakPtr()); CardUnmaskDelegate::UnmaskResponse response; response.cvc = base::ASCIIToUTF16("123"); response.exp_year = base::ASCIIToUTF16("2051"); response.exp_month = base::ASCIIToUTF16("12"); - ui_delegate()->OnUnmaskResponse(response); + card_unmask_delegate()->OnUnmaskResponse(response); OnDidGetRealPan(AutofillClient::SUCCESS, "4111"); - ui_delegate()->OnUnmaskPromptClosed(); + card_unmask_delegate()->OnUnmaskPromptClosed(); } // Verify getting the CVC for an unmasked server card with OK status, but // expiration date in the past. TEST_F(FullCardRequestTest, GetFullCardPanAndCvcForExpiredFullServerCard) { - EXPECT_CALL(*delegate(), OnFullCardRequestSucceeded( - CardMatches(CreditCard::FULL_SERVER_CARD, "4111", - "12", "2051"), - base::ASCIIToUTF16("123"))); - EXPECT_CALL(*client(), ShowUnmaskPrompt(_, _, _)); + EXPECT_CALL(*result_delegate(), OnFullCardRequestSucceeded( + CardMatches(CreditCard::FULL_SERVER_CARD, + "4111", "12", "2051"), + base::ASCIIToUTF16("123"))); + EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _)); EXPECT_CALL(*personal_data(), UpdateServerCreditCard(_)).Times(0); - EXPECT_CALL(*client(), OnUnmaskVerificationResult(AutofillClient::SUCCESS)); + EXPECT_CALL(*ui_delegate(), + OnUnmaskVerificationResult(AutofillClient::SUCCESS)); base::Time::Exploded today; base::Time::Now().LocalExplode(&today); @@ -232,203 +244,221 @@ TEST_F(FullCardRequestTest, GetFullCardPanAndCvcForExpiredFullServerCard) { base::StringPrintf("%d", today.year - 1).c_str()); full_server_card.SetServerStatus(CreditCard::OK); request()->GetFullCard(full_server_card, AutofillClient::UNMASK_FOR_AUTOFILL, - delegate()->AsWeakPtr()); + result_delegate()->AsWeakPtr(), + ui_delegate()->AsWeakPtr()); CardUnmaskDelegate::UnmaskResponse response; response.cvc = base::ASCIIToUTF16("123"); response.exp_year = base::ASCIIToUTF16("2051"); response.exp_month = base::ASCIIToUTF16("12"); - ui_delegate()->OnUnmaskResponse(response); + card_unmask_delegate()->OnUnmaskResponse(response); OnDidGetRealPan(AutofillClient::SUCCESS, "4111"); - ui_delegate()->OnUnmaskPromptClosed(); + card_unmask_delegate()->OnUnmaskPromptClosed(); } // Only one request at a time should be allowed. TEST_F(FullCardRequestTest, OneRequestAtATime) { - EXPECT_CALL(*delegate(), OnFullCardRequestFailed()); - EXPECT_CALL(*client(), ShowUnmaskPrompt(_, _, _)); - EXPECT_CALL(*client(), OnUnmaskVerificationResult(_)).Times(0); + EXPECT_CALL(*result_delegate(), OnFullCardRequestFailed()); + EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _)); + EXPECT_CALL(*ui_delegate(), OnUnmaskVerificationResult(_)).Times(0); request()->GetFullCard( CreditCard(CreditCard::MASKED_SERVER_CARD, "server_id_1"), - AutofillClient::UNMASK_FOR_AUTOFILL, delegate()->AsWeakPtr()); + AutofillClient::UNMASK_FOR_AUTOFILL, result_delegate()->AsWeakPtr(), + ui_delegate()->AsWeakPtr()); request()->GetFullCard( CreditCard(CreditCard::MASKED_SERVER_CARD, "server_id_2"), - AutofillClient::UNMASK_FOR_PAYMENT_REQUEST, delegate()->AsWeakPtr()); + AutofillClient::UNMASK_FOR_PAYMENT_REQUEST, + result_delegate()->AsWeakPtr(), ui_delegate()->AsWeakPtr()); } // After the first request completes, it's OK to start the second request. TEST_F(FullCardRequestTest, SecondRequestOkAfterFirstFinished) { - EXPECT_CALL(*delegate(), OnFullCardRequestFailed()).Times(0); - EXPECT_CALL(*delegate(), OnFullCardRequestSucceeded( - CardMatches(CreditCard::LOCAL_CARD, "4111"), - base::ASCIIToUTF16("123"))) + EXPECT_CALL(*result_delegate(), OnFullCardRequestFailed()).Times(0); + EXPECT_CALL( + *result_delegate(), + OnFullCardRequestSucceeded(CardMatches(CreditCard::LOCAL_CARD, "4111"), + base::ASCIIToUTF16("123"))) .Times(2); - EXPECT_CALL(*client(), ShowUnmaskPrompt(_, _, _)).Times(2); - EXPECT_CALL(*client(), OnUnmaskVerificationResult(AutofillClient::SUCCESS)) + EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _)).Times(2); + EXPECT_CALL(*ui_delegate(), + OnUnmaskVerificationResult(AutofillClient::SUCCESS)) .Times(2); CreditCard card; test::SetCreditCardInfo(&card, nullptr, "4111", "12", "2050"); request()->GetFullCard(card, AutofillClient::UNMASK_FOR_AUTOFILL, - delegate()->AsWeakPtr()); + result_delegate()->AsWeakPtr(), + ui_delegate()->AsWeakPtr()); CardUnmaskDelegate::UnmaskResponse response; response.cvc = base::ASCIIToUTF16("123"); - ui_delegate()->OnUnmaskResponse(response); - ui_delegate()->OnUnmaskPromptClosed(); + card_unmask_delegate()->OnUnmaskResponse(response); + card_unmask_delegate()->OnUnmaskPromptClosed(); request()->GetFullCard(card, AutofillClient::UNMASK_FOR_AUTOFILL, - delegate()->AsWeakPtr()); - ui_delegate()->OnUnmaskResponse(response); - ui_delegate()->OnUnmaskPromptClosed(); + result_delegate()->AsWeakPtr(), + ui_delegate()->AsWeakPtr()); + card_unmask_delegate()->OnUnmaskResponse(response); + card_unmask_delegate()->OnUnmaskPromptClosed(); } // If the user cancels the CVC prompt, // FullCardRequest::Delegate::OnFullCardRequestFailed() should be invoked. TEST_F(FullCardRequestTest, ClosePromptWithoutUserInput) { - EXPECT_CALL(*delegate(), OnFullCardRequestFailed()); - EXPECT_CALL(*client(), ShowUnmaskPrompt(_, _, _)); - EXPECT_CALL(*client(), OnUnmaskVerificationResult(_)).Times(0); + EXPECT_CALL(*result_delegate(), OnFullCardRequestFailed()); + EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _)); + EXPECT_CALL(*ui_delegate(), OnUnmaskVerificationResult(_)).Times(0); request()->GetFullCard( CreditCard(CreditCard::MASKED_SERVER_CARD, "server_id"), - AutofillClient::UNMASK_FOR_AUTOFILL, delegate()->AsWeakPtr()); - ui_delegate()->OnUnmaskPromptClosed(); + AutofillClient::UNMASK_FOR_AUTOFILL, result_delegate()->AsWeakPtr(), + ui_delegate()->AsWeakPtr()); + card_unmask_delegate()->OnUnmaskPromptClosed(); } // If the server provides an empty PAN with PERMANENT_FAILURE error, // FullCardRequest::Delegate::OnFullCardRequestFailed() should be invoked. TEST_F(FullCardRequestTest, PermanentFailure) { - EXPECT_CALL(*delegate(), OnFullCardRequestFailed()); - EXPECT_CALL(*client(), ShowUnmaskPrompt(_, _, _)); - EXPECT_CALL(*client(), + EXPECT_CALL(*result_delegate(), OnFullCardRequestFailed()); + EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _)); + EXPECT_CALL(*ui_delegate(), OnUnmaskVerificationResult(AutofillClient::PERMANENT_FAILURE)); request()->GetFullCard( CreditCard(CreditCard::MASKED_SERVER_CARD, "server_id"), - AutofillClient::UNMASK_FOR_AUTOFILL, delegate()->AsWeakPtr()); + AutofillClient::UNMASK_FOR_AUTOFILL, result_delegate()->AsWeakPtr(), + ui_delegate()->AsWeakPtr()); CardUnmaskDelegate::UnmaskResponse response; response.cvc = base::ASCIIToUTF16("123"); - ui_delegate()->OnUnmaskResponse(response); + card_unmask_delegate()->OnUnmaskResponse(response); OnDidGetRealPan(AutofillClient::PERMANENT_FAILURE, ""); - ui_delegate()->OnUnmaskPromptClosed(); + card_unmask_delegate()->OnUnmaskPromptClosed(); } // If the server provides an empty PAN with NETWORK_ERROR error, // FullCardRequest::Delegate::OnFullCardRequestFailed() should be invoked. TEST_F(FullCardRequestTest, NetworkError) { - EXPECT_CALL(*delegate(), OnFullCardRequestFailed()); - EXPECT_CALL(*client(), ShowUnmaskPrompt(_, _, _)); - EXPECT_CALL(*client(), + EXPECT_CALL(*result_delegate(), OnFullCardRequestFailed()); + EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _)); + EXPECT_CALL(*ui_delegate(), OnUnmaskVerificationResult(AutofillClient::NETWORK_ERROR)); request()->GetFullCard( CreditCard(CreditCard::MASKED_SERVER_CARD, "server_id"), - AutofillClient::UNMASK_FOR_AUTOFILL, delegate()->AsWeakPtr()); + AutofillClient::UNMASK_FOR_AUTOFILL, result_delegate()->AsWeakPtr(), + ui_delegate()->AsWeakPtr()); CardUnmaskDelegate::UnmaskResponse response; response.cvc = base::ASCIIToUTF16("123"); - ui_delegate()->OnUnmaskResponse(response); + card_unmask_delegate()->OnUnmaskResponse(response); OnDidGetRealPan(AutofillClient::NETWORK_ERROR, ""); - ui_delegate()->OnUnmaskPromptClosed(); + card_unmask_delegate()->OnUnmaskPromptClosed(); } // If the server provides an empty PAN with TRY_AGAIN_FAILURE, the user can // manually cancel out of the dialog. TEST_F(FullCardRequestTest, TryAgainFailureGiveUp) { - EXPECT_CALL(*delegate(), OnFullCardRequestFailed()); - EXPECT_CALL(*client(), ShowUnmaskPrompt(_, _, _)); - EXPECT_CALL(*client(), + EXPECT_CALL(*result_delegate(), OnFullCardRequestFailed()); + EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _)); + EXPECT_CALL(*ui_delegate(), OnUnmaskVerificationResult(AutofillClient::TRY_AGAIN_FAILURE)); request()->GetFullCard( CreditCard(CreditCard::MASKED_SERVER_CARD, "server_id"), - AutofillClient::UNMASK_FOR_AUTOFILL, delegate()->AsWeakPtr()); + AutofillClient::UNMASK_FOR_AUTOFILL, result_delegate()->AsWeakPtr(), + ui_delegate()->AsWeakPtr()); CardUnmaskDelegate::UnmaskResponse response; response.cvc = base::ASCIIToUTF16("123"); - ui_delegate()->OnUnmaskResponse(response); + card_unmask_delegate()->OnUnmaskResponse(response); OnDidGetRealPan(AutofillClient::TRY_AGAIN_FAILURE, ""); - ui_delegate()->OnUnmaskPromptClosed(); + card_unmask_delegate()->OnUnmaskPromptClosed(); } // If the server provides an empty PAN with TRY_AGAIN_FAILURE, the user can // correct their mistake and resubmit. TEST_F(FullCardRequestTest, TryAgainFailureRetry) { - EXPECT_CALL(*delegate(), OnFullCardRequestFailed()).Times(0); - EXPECT_CALL(*delegate(), + EXPECT_CALL(*result_delegate(), OnFullCardRequestFailed()).Times(0); + EXPECT_CALL(*result_delegate(), OnFullCardRequestSucceeded( CardMatches(CreditCard::FULL_SERVER_CARD, "4111"), base::ASCIIToUTF16("123"))); - EXPECT_CALL(*client(), ShowUnmaskPrompt(_, _, _)); - EXPECT_CALL(*client(), + EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _)); + EXPECT_CALL(*ui_delegate(), OnUnmaskVerificationResult(AutofillClient::TRY_AGAIN_FAILURE)); - EXPECT_CALL(*client(), OnUnmaskVerificationResult(AutofillClient::SUCCESS)); + EXPECT_CALL(*ui_delegate(), + OnUnmaskVerificationResult(AutofillClient::SUCCESS)); request()->GetFullCard( CreditCard(CreditCard::MASKED_SERVER_CARD, "server_id"), - AutofillClient::UNMASK_FOR_AUTOFILL, delegate()->AsWeakPtr()); + AutofillClient::UNMASK_FOR_AUTOFILL, result_delegate()->AsWeakPtr(), + ui_delegate()->AsWeakPtr()); CardUnmaskDelegate::UnmaskResponse response; response.cvc = base::ASCIIToUTF16("789"); - ui_delegate()->OnUnmaskResponse(response); + card_unmask_delegate()->OnUnmaskResponse(response); OnDidGetRealPan(AutofillClient::TRY_AGAIN_FAILURE, ""); response.cvc = base::ASCIIToUTF16("123"); - ui_delegate()->OnUnmaskResponse(response); + card_unmask_delegate()->OnUnmaskResponse(response); OnDidGetRealPan(AutofillClient::SUCCESS, "4111"); - ui_delegate()->OnUnmaskPromptClosed(); + card_unmask_delegate()->OnUnmaskPromptClosed(); } // Verify updating expiration date for a masked server card. TEST_F(FullCardRequestTest, UpdateExpDateForMaskedServerCard) { - EXPECT_CALL(*delegate(), OnFullCardRequestSucceeded( - CardMatches(CreditCard::FULL_SERVER_CARD, "4111", - "12", "2050"), - base::ASCIIToUTF16("123"))); - EXPECT_CALL(*client(), ShowUnmaskPrompt(_, _, _)); - EXPECT_CALL(*client(), OnUnmaskVerificationResult(AutofillClient::SUCCESS)); + EXPECT_CALL(*result_delegate(), OnFullCardRequestSucceeded( + CardMatches(CreditCard::FULL_SERVER_CARD, + "4111", "12", "2050"), + base::ASCIIToUTF16("123"))); + EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _)); + EXPECT_CALL(*ui_delegate(), + OnUnmaskVerificationResult(AutofillClient::SUCCESS)); request()->GetFullCard( CreditCard(CreditCard::MASKED_SERVER_CARD, "server_id"), - AutofillClient::UNMASK_FOR_AUTOFILL, delegate()->AsWeakPtr()); + AutofillClient::UNMASK_FOR_AUTOFILL, result_delegate()->AsWeakPtr(), + ui_delegate()->AsWeakPtr()); CardUnmaskDelegate::UnmaskResponse response; response.cvc = base::ASCIIToUTF16("123"); response.exp_month = base::ASCIIToUTF16("12"); response.exp_year = base::ASCIIToUTF16("2050"); - ui_delegate()->OnUnmaskResponse(response); + card_unmask_delegate()->OnUnmaskResponse(response); OnDidGetRealPan(AutofillClient::SUCCESS, "4111"); - ui_delegate()->OnUnmaskPromptClosed(); + card_unmask_delegate()->OnUnmaskPromptClosed(); } // Verify updating expiration date for an unmasked server card. TEST_F(FullCardRequestTest, UpdateExpDateForFullServerCard) { - EXPECT_CALL(*delegate(), OnFullCardRequestSucceeded( - CardMatches(CreditCard::FULL_SERVER_CARD, "4111", - "12", "2050"), - base::ASCIIToUTF16("123"))); - EXPECT_CALL(*client(), ShowUnmaskPrompt(_, _, _)); - EXPECT_CALL(*client(), OnUnmaskVerificationResult(AutofillClient::SUCCESS)); + EXPECT_CALL(*result_delegate(), OnFullCardRequestSucceeded( + CardMatches(CreditCard::FULL_SERVER_CARD, + "4111", "12", "2050"), + base::ASCIIToUTF16("123"))); + EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _)); + EXPECT_CALL(*ui_delegate(), + OnUnmaskVerificationResult(AutofillClient::SUCCESS)); CreditCard full_server_card(CreditCard::FULL_SERVER_CARD, "server_id"); test::SetCreditCardInfo(&full_server_card, nullptr, "4111", "10", "2000"); request()->GetFullCard(full_server_card, AutofillClient::UNMASK_FOR_AUTOFILL, - delegate()->AsWeakPtr()); + result_delegate()->AsWeakPtr(), + ui_delegate()->AsWeakPtr()); CardUnmaskDelegate::UnmaskResponse response; response.cvc = base::ASCIIToUTF16("123"); response.exp_month = base::ASCIIToUTF16("12"); response.exp_year = base::ASCIIToUTF16("2050"); - ui_delegate()->OnUnmaskResponse(response); + card_unmask_delegate()->OnUnmaskResponse(response); OnDidGetRealPan(AutofillClient::SUCCESS, "4111"); - ui_delegate()->OnUnmaskPromptClosed(); + card_unmask_delegate()->OnUnmaskPromptClosed(); } // Verify updating expiration date for a local card. TEST_F(FullCardRequestTest, UpdateExpDateForLocalCard) { - EXPECT_CALL(*delegate(), + EXPECT_CALL(*result_delegate(), OnFullCardRequestSucceeded( CardMatches(CreditCard::LOCAL_CARD, "4111", "12", "2051"), base::ASCIIToUTF16("123"))); - EXPECT_CALL(*client(), ShowUnmaskPrompt(_, _, _)); + EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _)); EXPECT_CALL(*personal_data(), UpdateCreditCard( CardMatches(CreditCard::LOCAL_CARD, "4111", "12", "2051"))); - EXPECT_CALL(*client(), OnUnmaskVerificationResult(AutofillClient::SUCCESS)); + EXPECT_CALL(*ui_delegate(), + OnUnmaskVerificationResult(AutofillClient::SUCCESS)); base::Time::Exploded today; base::Time::Now().LocalExplode(&today); @@ -436,80 +466,87 @@ TEST_F(FullCardRequestTest, UpdateExpDateForLocalCard) { test::SetCreditCardInfo(&card, nullptr, "4111", "10", base::StringPrintf("%d", today.year - 1).c_str()); request()->GetFullCard(card, AutofillClient::UNMASK_FOR_AUTOFILL, - delegate()->AsWeakPtr()); + result_delegate()->AsWeakPtr(), + ui_delegate()->AsWeakPtr()); CardUnmaskDelegate::UnmaskResponse response; response.cvc = base::ASCIIToUTF16("123"); response.exp_month = base::ASCIIToUTF16("12"); response.exp_year = base::ASCIIToUTF16("2051"); - ui_delegate()->OnUnmaskResponse(response); - ui_delegate()->OnUnmaskPromptClosed(); + card_unmask_delegate()->OnUnmaskResponse(response); + card_unmask_delegate()->OnUnmaskPromptClosed(); } // Verify saving full PAN on disk. TEST_F(FullCardRequestTest, SaveRealPan) { - EXPECT_CALL(*delegate(), OnFullCardRequestSucceeded( - CardMatches(CreditCard::FULL_SERVER_CARD, "4111", - "12", "2050"), - base::ASCIIToUTF16("123"))); - EXPECT_CALL(*client(), ShowUnmaskPrompt(_, _, _)); + EXPECT_CALL(*result_delegate(), OnFullCardRequestSucceeded( + CardMatches(CreditCard::FULL_SERVER_CARD, + "4111", "12", "2050"), + base::ASCIIToUTF16("123"))); + EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _)); EXPECT_CALL(*personal_data(), UpdateServerCreditCard(CardMatches(CreditCard::FULL_SERVER_CARD, "4111", "12", "2050"))); - EXPECT_CALL(*client(), OnUnmaskVerificationResult(AutofillClient::SUCCESS)); + EXPECT_CALL(*ui_delegate(), + OnUnmaskVerificationResult(AutofillClient::SUCCESS)); request()->GetFullCard( CreditCard(CreditCard::MASKED_SERVER_CARD, "server_id"), - AutofillClient::UNMASK_FOR_AUTOFILL, delegate()->AsWeakPtr()); + AutofillClient::UNMASK_FOR_AUTOFILL, result_delegate()->AsWeakPtr(), + ui_delegate()->AsWeakPtr()); CardUnmaskDelegate::UnmaskResponse response; response.cvc = base::ASCIIToUTF16("123"); response.exp_month = base::ASCIIToUTF16("12"); response.exp_year = base::ASCIIToUTF16("2050"); response.should_store_pan = true; - ui_delegate()->OnUnmaskResponse(response); + card_unmask_delegate()->OnUnmaskResponse(response); OnDidGetRealPan(AutofillClient::SUCCESS, "4111"); - ui_delegate()->OnUnmaskPromptClosed(); + card_unmask_delegate()->OnUnmaskPromptClosed(); } // Verify getting full PAN and CVC for PaymentRequest. TEST_F(FullCardRequestTest, UnmaskForPaymentRequest) { - EXPECT_CALL(*delegate(), + EXPECT_CALL(*result_delegate(), OnFullCardRequestSucceeded( CardMatches(CreditCard::FULL_SERVER_CARD, "4111"), base::ASCIIToUTF16("123"))); - EXPECT_CALL(*client(), ShowUnmaskPrompt(_, _, _)); - EXPECT_CALL(*client(), OnUnmaskVerificationResult(AutofillClient::SUCCESS)); + EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _)); + EXPECT_CALL(*ui_delegate(), + OnUnmaskVerificationResult(AutofillClient::SUCCESS)); request()->GetFullCard( CreditCard(CreditCard::MASKED_SERVER_CARD, "server_id"), - AutofillClient::UNMASK_FOR_PAYMENT_REQUEST, delegate()->AsWeakPtr()); + AutofillClient::UNMASK_FOR_PAYMENT_REQUEST, + result_delegate()->AsWeakPtr(), ui_delegate()->AsWeakPtr()); CardUnmaskDelegate::UnmaskResponse response; response.cvc = base::ASCIIToUTF16("123"); - ui_delegate()->OnUnmaskResponse(response); + card_unmask_delegate()->OnUnmaskResponse(response); OnDidGetRealPan(AutofillClient::SUCCESS, "4111"); - ui_delegate()->OnUnmaskPromptClosed(); + card_unmask_delegate()->OnUnmaskPromptClosed(); } // Verify that FullCardRequest::IsGettingFullCard() is true until the server // returns the full PAN for a masked card. TEST_F(FullCardRequestTest, IsGettingFullCardForMaskedServerCard) { - EXPECT_CALL(*delegate(), + EXPECT_CALL(*result_delegate(), OnFullCardRequestSucceeded( CardMatches(CreditCard::FULL_SERVER_CARD, "4111"), base::ASCIIToUTF16("123"))); - EXPECT_CALL(*client(), ShowUnmaskPrompt(_, _, _)); - EXPECT_CALL(*client(), OnUnmaskVerificationResult(AutofillClient::SUCCESS)); + EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _)); + EXPECT_CALL(*ui_delegate(), + OnUnmaskVerificationResult(AutofillClient::SUCCESS)); EXPECT_FALSE(request()->IsGettingFullCard()); request()->GetFullCard( CreditCard(CreditCard::MASKED_SERVER_CARD, "server_id"), - AutofillClient::UNMASK_FOR_AUTOFILL, delegate()->AsWeakPtr()); + AutofillClient::UNMASK_FOR_AUTOFILL, result_delegate()->AsWeakPtr(), + ui_delegate()->AsWeakPtr()); EXPECT_TRUE(request()->IsGettingFullCard()); CardUnmaskDelegate::UnmaskResponse response; response.cvc = base::ASCIIToUTF16("123"); - ui_delegate()->OnUnmaskResponse(response); + card_unmask_delegate()->OnUnmaskResponse(response); EXPECT_TRUE(request()->IsGettingFullCard()); @@ -517,7 +554,7 @@ TEST_F(FullCardRequestTest, IsGettingFullCardForMaskedServerCard) { EXPECT_FALSE(request()->IsGettingFullCard()); - ui_delegate()->OnUnmaskPromptClosed(); + card_unmask_delegate()->OnUnmaskPromptClosed(); EXPECT_FALSE(request()->IsGettingFullCard()); } @@ -525,28 +562,31 @@ TEST_F(FullCardRequestTest, IsGettingFullCardForMaskedServerCard) { // Verify that FullCardRequest::IsGettingFullCard() is true until the user types // in the CVC for a card that is not masked. TEST_F(FullCardRequestTest, IsGettingFullCardForLocalCard) { - EXPECT_CALL(*delegate(), OnFullCardRequestSucceeded( - CardMatches(CreditCard::LOCAL_CARD, "4111"), - base::ASCIIToUTF16("123"))); - EXPECT_CALL(*client(), ShowUnmaskPrompt(_, _, _)); - EXPECT_CALL(*client(), OnUnmaskVerificationResult(AutofillClient::SUCCESS)); + EXPECT_CALL( + *result_delegate(), + OnFullCardRequestSucceeded(CardMatches(CreditCard::LOCAL_CARD, "4111"), + base::ASCIIToUTF16("123"))); + EXPECT_CALL(*ui_delegate(), ShowUnmaskPrompt(_, _, _)); + EXPECT_CALL(*ui_delegate(), + OnUnmaskVerificationResult(AutofillClient::SUCCESS)); EXPECT_FALSE(request()->IsGettingFullCard()); CreditCard card; test::SetCreditCardInfo(&card, nullptr, "4111", "12", "2050"); request()->GetFullCard(card, AutofillClient::UNMASK_FOR_AUTOFILL, - delegate()->AsWeakPtr()); + result_delegate()->AsWeakPtr(), + ui_delegate()->AsWeakPtr()); EXPECT_TRUE(request()->IsGettingFullCard()); CardUnmaskDelegate::UnmaskResponse response; response.cvc = base::ASCIIToUTF16("123"); - ui_delegate()->OnUnmaskResponse(response); + card_unmask_delegate()->OnUnmaskResponse(response); EXPECT_FALSE(request()->IsGettingFullCard()); - ui_delegate()->OnUnmaskPromptClosed(); + card_unmask_delegate()->OnUnmaskPromptClosed(); EXPECT_FALSE(request()->IsGettingFullCard()); } diff --git a/chromium/components/autofill/core/browser/payments/payments_service_url.cc b/chromium/components/autofill/core/browser/payments/payments_service_url.cc index 2ea46233df0..f545e9a16d7 100644 --- a/chromium/components/autofill/core/browser/payments/payments_service_url.cc +++ b/chromium/components/autofill/core/browser/payments/payments_service_url.cc @@ -21,10 +21,10 @@ namespace autofill { namespace { -const char kProdPaymentsServiceUrl[] = "https://wallet.google.com/"; +const char kProdPaymentsServiceUrl[] = "https://payments.google.com/"; const char kSandboxPaymentsSecureServiceUrl[] = - "https://wallet-web.sandbox.google.com/"; + "https://payments.sandbox.google.com/"; } // namespace @@ -46,14 +46,13 @@ GURL GetBaseSecureUrl() { GURL GetManageInstrumentsUrl(size_t user_index) { std::string path = - base::StringPrintf("manage/w/%" PRIuS "/paymentMethods", user_index); + base::StringPrintf("u/%" PRIuS "#paymentMethods", user_index); return GetBaseSecureUrl().Resolve(path); } GURL GetManageAddressesUrl(size_t user_index) { - std::string path = - base::StringPrintf("manage/w/%" PRIuS "/settings/addresses", user_index); - return GetBaseSecureUrl().Resolve(path); + // Billing addresses are now managed as a part of the payment instrument. + return GetManageInstrumentsUrl(user_index); } } // namespace payments diff --git a/chromium/components/autofill/core/browser/payments/payments_service_url_unittest.cc b/chromium/components/autofill/core/browser/payments/payments_service_url_unittest.cc index 09dc91b5b86..8cdc5001257 100644 --- a/chromium/components/autofill/core/browser/payments/payments_service_url_unittest.cc +++ b/chromium/components/autofill/core/browser/payments/payments_service_url_unittest.cc @@ -15,21 +15,21 @@ TEST(PaymentsServiceSandboxUrl, CheckSandboxUrls) { base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( switches::kWalletServiceUseSandbox, "1"); - EXPECT_EQ("https://wallet-web.sandbox.google.com/manage/w/1/paymentMethods", - GetManageInstrumentsUrl(1).spec()); - EXPECT_EQ("https://wallet-web.sandbox.google.com/manage/w/1/settings/" - "addresses", - GetManageAddressesUrl(1).spec()); + const char kExpectedSandboxURL[] = + "https://payments.sandbox.google.com/u/1#paymentMethods"; + + EXPECT_EQ(kExpectedSandboxURL, GetManageInstrumentsUrl(1).spec()); + EXPECT_EQ(kExpectedSandboxURL, GetManageAddressesUrl(1).spec()); } TEST(PaymentsServiceSandboxUrl, CheckProdUrls) { base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( switches::kWalletServiceUseSandbox, "0"); - EXPECT_EQ("https://wallet.google.com/manage/w/1/paymentMethods", - GetManageInstrumentsUrl(1).spec()); - EXPECT_EQ("https://wallet.google.com/manage/w/1/settings/addresses", - GetManageAddressesUrl(1).spec()); + const char kExpectedURL[] = "https://payments.google.com/u/1#paymentMethods"; + + EXPECT_EQ(kExpectedURL, GetManageInstrumentsUrl(1).spec()); + EXPECT_EQ(kExpectedURL, GetManageAddressesUrl(1).spec()); } } // namespace payments diff --git a/chromium/components/autofill/core/browser/personal_data_manager.cc b/chromium/components/autofill/core/browser/personal_data_manager.cc index 88096ec268a..1ea5a2f555f 100644 --- a/chromium/components/autofill/core/browser/personal_data_manager.cc +++ b/chromium/components/autofill/core/browser/personal_data_manager.cc @@ -34,6 +34,7 @@ #include "components/autofill/core/browser/phone_number.h" #include "components/autofill/core/browser/phone_number_i18n.h" #include "components/autofill/core/browser/validation.h" +#include "components/autofill/core/common/autofill_clock.h" #include "components/autofill/core/common/autofill_pref_names.h" #include "components/autofill/core/common/autofill_switches.h" #include "components/autofill/core/common/autofill_util.h" @@ -54,6 +55,9 @@ using ::i18n::addressinput::AddressField; using ::i18n::addressinput::GetStreetAddressLinesAsSingleLine; using ::i18n::addressinput::STREET_ADDRESS; +// The length of a local profile GUID. +const int LOCAL_GUID_LENGTH = 36; + template<typename T> class FormGroupMatchesByGUIDFunctor { public: @@ -254,15 +258,15 @@ const char kFrecencyFieldTrialName[] = "AutofillProfileOrderByFrecency"; const char kFrecencyFieldTrialLimitParam[] = "limit"; PersonalDataManager::PersonalDataManager(const std::string& app_locale) - : database_(NULL), + : database_(nullptr), is_data_loaded_(false), pending_profiles_query_(0), pending_server_profiles_query_(0), pending_creditcards_query_(0), pending_server_creditcards_query_(0), app_locale_(app_locale), - pref_service_(NULL), - account_tracker_(NULL), + pref_service_(nullptr), + account_tracker_(nullptr), is_off_the_record_(false), has_logged_profile_count_(false), has_logged_local_credit_card_count_(false), @@ -373,21 +377,6 @@ void PersonalDataManager::OnWebDataServiceRequestDone( } else { ReceiveLoadedDbValues(h, result.get(), &pending_server_profiles_query_, &server_profiles_); - - if (!server_profiles_.empty()) { - std::string account_id = signin_manager_->GetAuthenticatedAccountId(); - base::string16 email = - base::UTF8ToUTF16( - account_tracker_->GetAccountInfo(account_id).email); - - // User may have signed out during the fulfillment of the web data - // request, in which case there is no point updating - // |server_profiles_| as it will be cleared. - if (!email.empty()) { - for (auto& profile : server_profiles_) - profile->SetRawInfo(EMAIL_ADDRESS, email); - } - } } break; case AUTOFILL_CREDITCARDS_RESULT: @@ -423,6 +412,7 @@ void PersonalDataManager::OnWebDataServiceRequestDone( } void PersonalDataManager::AutofillMultipleChanged() { + has_synced_new_data_ = true; Refresh(); } @@ -554,10 +544,16 @@ void PersonalDataManager::UpdateProfile(const AutofillProfile& profile) { AutofillProfile* PersonalDataManager::GetProfileByGUID( const std::string& guid) { - const std::vector<AutofillProfile*>& profiles = GetProfiles(); + return GetProfileFromProfilesByGUID(guid, GetProfiles()); +} + +// static +AutofillProfile* PersonalDataManager::GetProfileFromProfilesByGUID( + const std::string& guid, + const std::vector<AutofillProfile*>& profiles) { std::vector<AutofillProfile*>::const_iterator iter = FindElementByGUID<AutofillProfile>(profiles, guid); - return (iter != profiles.end()) ? *iter : NULL; + return iter != profiles.end() ? *iter : nullptr; } void PersonalDataManager::AddCreditCard(const CreditCard& credit_card) { @@ -726,7 +722,7 @@ CreditCard* PersonalDataManager::GetCreditCardByGUID(const std::string& guid) { const std::vector<CreditCard*>& credit_cards = GetCreditCards(); std::vector<CreditCard*>::const_iterator iter = FindElementByGUID<CreditCard>(credit_cards, guid); - return (iter != credit_cards.end()) ? *iter : NULL; + return iter != credit_cards.end() ? *iter : nullptr; } void PersonalDataManager::GetNonEmptyTypes( @@ -752,6 +748,13 @@ std::vector<AutofillProfile*> PersonalDataManager::web_profiles() const { return result; } +std::vector<AutofillProfile*> PersonalDataManager::GetServerProfiles() const { + std::vector<AutofillProfile*> result; + for (const auto& profile : server_profiles_) + result.push_back(profile.get()); + return result; +} + std::vector<CreditCard*> PersonalDataManager::GetLocalCreditCards() const { std::vector<CreditCard*> result; for (const auto& card : local_credit_cards_) @@ -770,10 +773,6 @@ const std::vector<CreditCard*>& PersonalDataManager::GetCreditCards() const { return credit_cards_; } -bool PersonalDataManager::HasServerData() const { - return !server_credit_cards_.empty() || !server_profiles_.empty(); -} - void PersonalDataManager::Refresh() { LoadProfiles(); LoadCreditCards(); @@ -784,7 +783,7 @@ const std::vector<AutofillProfile*> PersonalDataManager::GetProfilesToSuggest() std::vector<AutofillProfile*> profiles = GetProfiles(true); // Rank the suggestions by frecency (see AutofillDataModel for details). - base::Time comparison_time = base::Time::Now(); + base::Time comparison_time = AutofillClock::Now(); std::sort(profiles.begin(), profiles.end(), [comparison_time](const AutofillDataModel* a, const AutofillDataModel* b) { @@ -908,7 +907,7 @@ const std::vector<CreditCard*> PersonalDataManager::GetCreditCardsToSuggest() // Rank the cards by frecency (see AutofillDataModel for details). All expired // cards should be suggested last, also by frecency. - base::Time comparison_time = base::Time::Now(); + base::Time comparison_time = AutofillClock::Now(); std::stable_sort(cards_to_suggest.begin(), cards_to_suggest.end(), [comparison_time](const CreditCard* a, const CreditCard* b) { bool a_is_expired = a->IsExpired(comparison_time); @@ -943,7 +942,7 @@ void PersonalDataManager::SetPrefService(PrefService* pref_service) { enabled_pref_.reset(new BooleanPrefMember); wallet_enabled_pref_.reset(new BooleanPrefMember); pref_service_ = pref_service; - // |pref_service_| can be NULL in tests. + // |pref_service_| can be nullptr in tests. if (pref_service_) { enabled_pref_->Init(prefs::kAutofillEnabled, pref_service_, base::Bind(&PersonalDataManager::EnabledPrefChanged, @@ -991,7 +990,7 @@ std::string PersonalDataManager::MergeProfile( // verified profiles get deduped among themselves before reaching the verified // profiles. // TODO(crbug.com/620521): Remove the check for verified from the sort. - base::Time comparison_time = base::Time::Now(); + base::Time comparison_time = AutofillClock::Now(); std::sort(existing_profiles->begin(), existing_profiles->end(), [comparison_time](const std::unique_ptr<AutofillProfile>& a, const std::unique_ptr<AutofillProfile>& b) { @@ -1024,7 +1023,7 @@ std::string PersonalDataManager::MergeProfile( // recently. After writing to the database and refreshing the local copies // the profile will have a very slightly newer time reflecting what's // actually stored in the database. - existing_profile->set_modification_date(base::Time::Now()); + existing_profile->set_modification_date(AutofillClock::Now()); } merged_profiles->push_back(*existing_profile); } @@ -1034,7 +1033,7 @@ std::string PersonalDataManager::MergeProfile( merged_profiles->push_back(new_profile); // Similar to updating merged profiles above, set the modification date on // new profiles. - merged_profiles->back().set_modification_date(base::Time::Now()); + merged_profiles->back().set_modification_date(AutofillClock::Now()); AutofillMetrics::LogProfileActionOnFormSubmitted( AutofillMetrics::NEW_PROFILE_CREATED); } @@ -1239,15 +1238,6 @@ std::string PersonalDataManager::SaveImportedProfile( if (is_off_the_record_) return std::string(); - // Don't save a web profile if the data in the profile is a subset of a - // server profile, but do record the fact that it was used. - for (const auto& profile : server_profiles_) { - if (imported_profile.IsSubsetOf(*profile, app_locale_)) { - RecordUseOf(*profile); - return profile->guid(); - } - } - std::vector<AutofillProfile> profiles; std::string guid = MergeProfile(imported_profile, &web_profiles_, app_locale_, &profiles); @@ -1258,6 +1248,13 @@ std::string PersonalDataManager::SaveImportedProfile( void PersonalDataManager::NotifyPersonalDataChanged() { for (PersonalDataManagerObserver& observer : observers_) observer.OnPersonalDataChanged(); + + // If new data was synced, try to convert new server profiles and update + // server cards. + if (has_synced_new_data_) { + has_synced_new_data_ = false; + ConvertWalletAddressesAndUpdateWalletCards(); + } } std::string PersonalDataManager::SaveImportedCreditCard( @@ -1575,10 +1572,6 @@ const std::vector<AutofillProfile*>& PersonalDataManager::GetProfiles( profiles_.clear(); for (const auto& profile : web_profiles_) profiles_.push_back(profile.get()); - if (pref_service_->GetBoolean(prefs::kAutofillWalletImportEnabled)) { - for (const auto& profile : server_profiles_) - profiles_.push_back(profile.get()); - } return profiles_; } @@ -1618,8 +1611,13 @@ std::vector<Suggestion> PersonalDataManager::GetSuggestionsForCards( // cardholder name. The label should never repeat the value. if (type.GetStorableType() == CREDIT_CARD_NUMBER) { suggestion->value = credit_card->TypeAndLastFourDigits(); - suggestion->label = credit_card->GetInfo( - AutofillType(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR), app_locale_); + if (IsAutofillCreditCardLastUsedDateDisplayExperimentEnabled()) { + suggestion->label = + credit_card->GetLastUsedDateForDisplay(app_locale_); + } else { + 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()) { @@ -1661,7 +1659,8 @@ void PersonalDataManager::ApplyProfileUseDatesFix() { bool has_changed_data = false; for (AutofillProfile* profile : web_profiles()) { if (profile->use_date() == base::Time()) { - profile->set_use_date(base::Time::Now() - base::TimeDelta::FromDays(14)); + profile->set_use_date(AutofillClock::Now() - + base::TimeDelta::FromDays(14)); has_changed_data = true; } profiles.push_back(*profile); @@ -1742,7 +1741,7 @@ void PersonalDataManager::DedupeProfiles( // profiles, so the loop can be stopped when we reach those. However they need // to be in the vector because an unverified profile trying to merge into a // similar verified profile will be discarded. - base::Time comparison_time = base::Time::Now(); + base::Time comparison_time = AutofillClock::Now(); std::sort(existing_profiles->begin(), existing_profiles->end(), [comparison_time](const std::unique_ptr<AutofillProfile>& a, const std::unique_ptr<AutofillProfile>& b) { @@ -1820,7 +1819,7 @@ void PersonalDataManager::UpdateCardsBillingAddressReference( C -> D */ - for (auto& credit_card : local_credit_cards_) { + for (auto* credit_card : GetCreditCards()) { // If the credit card is not associated with a billing address, skip it. if (credit_card->billing_address_id().empty()) break; @@ -1847,9 +1846,176 @@ void PersonalDataManager::UpdateCardsBillingAddressReference( // If the card was modified, apply the changes to the database. if (was_modified) { - database_->UpdateCreditCard(*credit_card); + if (credit_card->record_type() == CreditCard::LOCAL_CARD) + database_->UpdateCreditCard(*credit_card); + else + database_->UpdateServerCardMetadata(*credit_card); + } + } +} + +void PersonalDataManager::ConvertWalletAddressesAndUpdateWalletCards() { + // Copy the local profiles into a vector<AutofillProfile>. Theses are the + // existing profiles. Get them sorted in decreasing order of frecency, so the + // "best" profiles are checked first. Put the verified profiles last so the + // server addresses have a chance to merge into the non-verified local + // profiles. + std::vector<AutofillProfile> local_profiles; + for (AutofillProfile* existing_profile : GetProfilesToSuggest()) { + local_profiles.push_back(*existing_profile); + } + + // Since we are already iterating on all the server profiles to convert Wallet + // addresses and we will need to access them by guid later to update the + // Wallet cards, create a map here. + std::unordered_map<std::string, AutofillProfile*> server_id_profiles_map; + + // Create the map used to update credit card's billing addresses after the + // convertion/merge. + std::unordered_map<std::string, std::string> guids_merge_map; + + bool has_converted_addresses = ConvertWalletAddressesToLocalProfiles( + &local_profiles, &server_id_profiles_map, &guids_merge_map); + bool should_update_cards = UpdateWalletCardsAlreadyConvertedBillingAddresses( + &local_profiles, &server_id_profiles_map, &guids_merge_map); + + if (has_converted_addresses) { + // Save the local profiles to the DB. + SetProfiles(&local_profiles); + } + + if (should_update_cards || has_converted_addresses) { + // Update the credit cards billing address relationship. + UpdateCardsBillingAddressReference(guids_merge_map); + + // Force a reload of the profiles and cards. + Refresh(); + } +} + +bool PersonalDataManager::ConvertWalletAddressesToLocalProfiles( + std::vector<AutofillProfile>* local_profiles, + std::unordered_map<std::string, AutofillProfile*>* server_id_profiles_map, + std::unordered_map<std::string, std::string>* guids_merge_map) { + bool has_converted_addresses = false; + for (std::unique_ptr<AutofillProfile>& wallet_address : server_profiles_) { + // Add the profile to the map. + server_id_profiles_map->insert( + std::make_pair(wallet_address->server_id(), wallet_address.get())); + + // If the address has not been converted yet, convert it. + if (!wallet_address->has_converted()) { + // Try to merge the server address into a similar local profile, or create + // a new local profile if no similar profile is found. + std::string address_guid = + MergeServerAddressesIntoProfiles(*wallet_address, local_profiles); + + // Update the map to transfer the billing address relationship from the + // server address to the converted/merged local profile. + guids_merge_map->insert(std::pair<std::string, std::string>( + wallet_address->server_id(), address_guid)); + + // Update the wallet addresses metadata to record the conversion. + wallet_address->set_has_converted(true); + database_->UpdateServerAddressMetadata(*wallet_address); + + has_converted_addresses = true; + } + } + + return has_converted_addresses; +} + +bool PersonalDataManager::UpdateWalletCardsAlreadyConvertedBillingAddresses( + std::vector<AutofillProfile>* local_profiles, + std::unordered_map<std::string, AutofillProfile*>* server_id_profiles_map, + std::unordered_map<std::string, std::string>* guids_merge_map) { + // Look for server cards that still refer to server addresses but for which + // there is no mapping. This can happen if it's a new card for which the + // billing address has already been converted. This should be a no-op for most + // situations. Otherwise, it should affect only one Wallet card, sinces users + // do not add a lot of credit cards. + AutofillProfileComparator comparator(app_locale_); + bool should_update_cards = false; + for (std::unique_ptr<CreditCard>& wallet_card : server_credit_cards_) { + std::string billing_address_id = wallet_card->billing_address_id(); + + // If billing address refers to a server id and that id is not a key in the + // guids_merge_map, it means that the card is new but the address was + // already converted. Look for the matching converted profile. + if (!billing_address_id.empty() && + billing_address_id.length() != LOCAL_GUID_LENGTH && + guids_merge_map->find(billing_address_id) == guids_merge_map->end()) { + // Get the profile. + auto it = server_id_profiles_map->find(billing_address_id); + if (it != server_id_profiles_map->end()) { + AutofillProfile* billing_address = it->second; + + // Look for a matching local profile (DO NOT MERGE). + bool matching_profile_found = false; + for (auto& local_profile : *local_profiles) { + if (!matching_profile_found && + comparator.AreMergeable(*billing_address, local_profile)) { + matching_profile_found = true; + + // The Wallet address matches this local profile. Add this to the + // merge mapping. + guids_merge_map->insert(std::pair<std::string, std::string>( + billing_address_id, local_profile.guid())); + should_update_cards = true; + } + } + } } } + + return should_update_cards; +} + +// TODO(crbug.com/687975): Reuse MergeProfiles in this function. +std::string PersonalDataManager::MergeServerAddressesIntoProfiles( + const AutofillProfile& server_address, + std::vector<AutofillProfile>* existing_profiles) { + // Set to true if |existing_profiles| already contains an equivalent profile. + bool matching_profile_found = false; + std::string guid = server_address.guid(); + + // If there is already a local profile that is very similar, merge in any + // missing values. Only merge with the first match. + AutofillProfileComparator comparator(app_locale_); + for (auto& local_profile : *existing_profiles) { + if (!matching_profile_found && + comparator.AreMergeable(server_address, local_profile) && + local_profile.SaveAdditionalInfo(server_address, app_locale_)) { + matching_profile_found = true; + local_profile.set_modification_date(AutofillClock::Now()); + guid = local_profile.guid(); + AutofillMetrics::LogWalletAddressConversionType( + AutofillMetrics::CONVERTED_ADDRESS_MERGED); + } + } + + // If the server address was not merged with a local profile, add it to the + // list. + if (!matching_profile_found) { + existing_profiles->push_back(server_address); + // Set the profile as being local. + existing_profiles->back().set_record_type(AutofillProfile::LOCAL_PROFILE); + existing_profiles->back().set_modification_date(AutofillClock::Now()); + + // Wallet addresses don't have an email address, use the one from the + // currently signed-in account. + std::string account_id = signin_manager_->GetAuthenticatedAccountId(); + base::string16 email = + base::UTF8ToUTF16(account_tracker_->GetAccountInfo(account_id).email); + if (!email.empty()) + existing_profiles->back().SetRawInfo(EMAIL_ADDRESS, email); + + AutofillMetrics::LogWalletAddressConversionType( + AutofillMetrics::CONVERTED_ADDRESS_ADDED); + } + + return guid; } } // 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 165862f6217..a7f9808e53a 100644 --- a/chromium/components/autofill/core/browser/personal_data_manager.h +++ b/chromium/components/autofill/core/browser/personal_data_manager.h @@ -136,11 +136,17 @@ class PersonalDataManager : public KeyedService, // Removes the profile or credit card represented by |guid|. virtual void RemoveByGUID(const std::string& guid); - // Returns the profile with the specified |guid|, or NULL if there is no + // Returns the profile with the specified |guid|, or nullptr if there is no // profile with the specified |guid|. Both web and auxiliary profiles may // be returned. AutofillProfile* GetProfileByGUID(const std::string& guid); + // Returns the profile with the specified |guid| from the given |profiles|, or + // nullptr if there is no profile with the specified |guid|. + static AutofillProfile* GetProfileFromProfilesByGUID( + const std::string& guid, + const std::vector<AutofillProfile*>& profiles); + // Adds |credit_card| to the web database. void AddCreditCard(const CreditCard& credit_card); @@ -168,7 +174,7 @@ class PersonalDataManager : public KeyedService, // Sets a server credit card for test. void AddServerCreditCardForTest(std::unique_ptr<CreditCard> credit_card); - // Returns the credit card with the specified |guid|, or NULL if there is + // Returns the credit card with the specified |guid|, or nullptr if there is // no credit card with the specified |guid|. virtual CreditCard* GetCreditCardByGUID(const std::string& guid); @@ -183,18 +189,18 @@ class PersonalDataManager : public KeyedService, // This PersonalDataManager owns these profiles and credit cards. Their // lifetime is until the web database is updated with new profile and credit - // card information, respectively. |GetProfiles()| returns both web and - // auxiliary profiles. |web_profiles()| returns only web profiles. + // card information, respectively. + // TODO(crbug.com/687352): Remove one of these since they do the same thing. + // |GetProfiles()| and |web_profiles()| returns only local profiles. virtual const std::vector<AutofillProfile*>& GetProfiles() const; virtual std::vector<AutofillProfile*> web_profiles() const; + // Returns just SERVER_PROFILES. + virtual std::vector<AutofillProfile*> GetServerProfiles() const; // Returns just LOCAL_CARD cards. virtual std::vector<CreditCard*> GetLocalCreditCards() const; // Returns all credit cards, server and local. virtual const std::vector<CreditCard*>& GetCreditCards() const; - // Returns true if there is some data synced from Wallet. - bool HasServerData() const; - // Returns the profiles to suggest to the user, ordered by frecency. const std::vector<AutofillProfile*> GetProfilesToSuggest() const; @@ -316,6 +322,21 @@ class PersonalDataManager : public KeyedService, ApplyDedupingRoutine_OncePerVersion); FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest, ApplyDedupingRoutine_MultipleDedupes); + FRIEND_TEST_ALL_PREFIXES( + PersonalDataManagerTest, + ConvertWalletAddressesAndUpdateWalletCards_NewProfile); + FRIEND_TEST_ALL_PREFIXES( + PersonalDataManagerTest, + ConvertWalletAddressesAndUpdateWalletCards_MergedProfile); + FRIEND_TEST_ALL_PREFIXES( + PersonalDataManagerTest, + ConvertWalletAddressesAndUpdateWalletCards_NewCard_AddressAlreadyConverted); + FRIEND_TEST_ALL_PREFIXES( + PersonalDataManagerTest, + ConvertWalletAddressesAndUpdateWalletCards_AlreadyConverted); + FRIEND_TEST_ALL_PREFIXES( + PersonalDataManagerTest, + ConvertWalletAddressesAndUpdateWalletCards_MultipleSimilarWalletAddresses); friend class autofill::AutofillInteractiveTest; friend class autofill::AutofillTest; friend class autofill::PersonalDataManagerFactory; @@ -416,7 +437,7 @@ class PersonalDataManager : public KeyedService, std::vector<std::unique_ptr<CreditCard>> server_credit_cards_; // A combination of local and server credit cards. The pointers are owned - // by the local/sverver_credit_cards_ vectors. + // by the local/server_credit_cards_ vectors. mutable std::vector<CreditCard*> credit_cards_; // When the manager makes a request from WebDataServiceBase, the database @@ -462,7 +483,7 @@ class PersonalDataManager : public KeyedService, // Functionally equivalent to GetProfiles(), but also records metrics if // |record_metrics| is true. Metrics should be recorded when the returned // profiles will be used to populate the fields shown in an Autofill popup. - const std::vector<AutofillProfile*>& GetProfiles( + virtual const std::vector<AutofillProfile*>& GetProfiles( bool record_metrics) const; // Returns credit card suggestions based on the |cards_to_suggest| and the @@ -502,6 +523,41 @@ class PersonalDataManager : public KeyedService, void UpdateCardsBillingAddressReference( const std::unordered_map<std::string, std::string>& guids_merge_map); + // Converts the Wallet addresses to local autofill profiles. This should be + // called after all the syncable data has been processed (local cards and + // profiles, Wallet data and metadata). Also updates Wallet cards' billing + // address id to point to the local profiles. + void ConvertWalletAddressesAndUpdateWalletCards(); + + // Converts the Wallet addresses into local profiles either by merging with an + // existing |local_profiles| of by adding a new one. Populates the + // |server_id_profiles_map| to be used when updating cards where the address + // was already converted. Also populates the |guids_merge_map| to keep the + // link between the Wallet address and the equivalent local profile (from + // merge or creation). + bool ConvertWalletAddressesToLocalProfiles( + std::vector<AutofillProfile>* local_profiles, + std::unordered_map<std::string, AutofillProfile*>* server_id_profiles_map, + std::unordered_map<std::string, std::string>* guids_merge_map); + + // Goes through the Wallet cards to find cards where the billing address is a + // Wallet address which was already converted in a previous pass. Looks for a + // matching local profile and updates the |guids_merge_map| to make the card + // refert to it. + bool UpdateWalletCardsAlreadyConvertedBillingAddresses( + std::vector<AutofillProfile>* local_profiles, + std::unordered_map<std::string, AutofillProfile*>* server_id_profiles_map, + std::unordered_map<std::string, std::string>* guids_merge_map); + + // Tries to merge the |server_address| into the |existing_profiles| if + // possible. Adds it to the list if no match is found. The existing profiles + // should be sorted by decreasing frecency outside of this method, since this + // will be called multiple times in a row. Returns the guid of the new or + // updated profile. + std::string MergeServerAddressesIntoProfiles( + const AutofillProfile& server_address, + std::vector<AutofillProfile>* existing_profiles); + const std::string app_locale_; // The default country code for new addresses. @@ -541,6 +597,9 @@ class PersonalDataManager : public KeyedService, // True if autofill profile cleanup needs to be performed. bool is_autofill_profile_cleanup_pending_ = false; + // Whether new information was received from the sync server. + bool has_synced_new_data_ = false; + #if defined(OS_ANDROID) // The context for the request to be used to fetch libaddressinput's address // validation rules. 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 18bb0aebdd4..6bb7a52b7e0 100644 --- a/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc @@ -17,23 +17,27 @@ #include "base/command_line.h" #include "base/files/scoped_temp_dir.h" #include "base/guid.h" +#include "base/i18n/time_formatting.h" #include "base/memory/ptr_util.h" -#include "base/metrics/field_trial.h" #include "base/run_loop.h" #include "base/strings/utf_string_conversions.h" #include "base/synchronization/waitable_event.h" #include "base/test/histogram_tester.h" +#include "base/test/simple_test_clock.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" #include "build/build_config.h" #include "components/autofill/core/browser/autofill_experiments.h" +#include "components/autofill/core/browser/autofill_metrics.h" #include "components/autofill/core/browser/autofill_profile.h" #include "components/autofill/core/browser/autofill_test_utils.h" #include "components/autofill/core/browser/field_types.h" #include "components/autofill/core/browser/form_structure.h" #include "components/autofill/core/browser/personal_data_manager_observer.h" +#include "components/autofill/core/browser/test_autofill_clock.h" #include "components/autofill/core/browser/webdata/autofill_table.h" #include "components/autofill/core/browser/webdata/autofill_webdata_service.h" +#include "components/autofill/core/common/autofill_clock.h" #include "components/autofill/core/common/autofill_constants.h" #include "components/autofill/core/common/autofill_pref_names.h" #include "components/autofill/core/common/autofill_switches.h" @@ -44,8 +48,7 @@ #include "components/signin/core/browser/fake_signin_manager.h" #include "components/signin/core/browser/test_signin_client.h" #include "components/signin/core/common/signin_pref_names.h" -#include "components/variations/entropy_provider.h" -#include "components/variations/variations_associated_data.h" +#include "components/variations/variations_params_manager.h" #include "components/webdata/common/web_data_service_base.h" #include "components/webdata/common/web_database_service.h" #include "testing/gmock/include/gmock/gmock.h" @@ -66,6 +69,10 @@ const std::string kUTF8MidlineEllipsis = "\xE2\x80\xA2\xE2\x80\x86" "\xE2\x80\xA2\xE2\x80\x86"; +const base::Time kArbitraryTime = base::Time::FromDoubleT(25); +const base::Time kSomeLaterTime = base::Time::FromDoubleT(1000); +const base::Time kMuchLaterTime = base::Time::FromDoubleT(5000); + ACTION(QuitMainMessageLoop) { base::MessageLoop::current()->QuitWhenIdle(); } @@ -146,9 +153,6 @@ class PersonalDataManagerTest : public testing::Test { test::DisableSystemServices(prefs_.get()); ResetPersonalDataManager(USER_MODE_NORMAL); - // There are no field trials enabled by default. - field_trial_list_.reset(); - // Reset the deduping pref to its default value. personal_data_->pref_service_->SetInteger( prefs::kAutofillLastVersionDeduped, 0); @@ -263,13 +267,14 @@ class PersonalDataManagerTest : public testing::Test { "347666888555" /* American Express */, "04", "2999"); credit_card0.set_use_count(3); - credit_card0.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(1)); + credit_card0.set_use_date(AutofillClock::Now() - + base::TimeDelta::FromDays(1)); personal_data_->AddCreditCard(credit_card0); CreditCard credit_card1("1141084B-72D7-4B73-90CF-3D6AC154673B", "https://www.example.com"); credit_card1.set_use_count(300); - credit_card1.set_use_date(base::Time::Now() - + credit_card1.set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(10)); test::SetCreditCardInfo(&credit_card1, "John Dillinger", "423456789012" /* Visa */, "01", "2999"); @@ -278,7 +283,8 @@ class PersonalDataManagerTest : public testing::Test { CreditCard credit_card2("002149C1-EE28-4213-A3B9-DA243FFF021B", "https://www.example.com"); credit_card2.set_use_count(1); - credit_card2.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(1)); + credit_card2.set_use_date(AutofillClock::Now() - + base::TimeDelta::FromDays(1)); test::SetCreditCardInfo(&credit_card2, "Bonnie Parker", "518765432109" /* Mastercard */, "12", "2999"); personal_data_->AddCreditCard(credit_card2); @@ -303,28 +309,6 @@ class PersonalDataManagerTest : public testing::Test { imported_credit_card); } - // Sets up the profile order field trial group and parameter. Sets up the - // suggestions limit parameter to |limit_param|. - void EnableAutofillProfileLimitFieldTrial(const std::string& limit_param) { - DCHECK(!limit_param.empty()); - - // Clear the existing |field_trial_list_| and variation parameters. - field_trial_list_.reset(NULL); - field_trial_list_.reset( - new base::FieldTrialList( - base::MakeUnique<metrics::SHA1EntropyProvider>("foo"))); - variations::testing::ClearAllVariationParams(); - - std::map<std::string, std::string> params; - params[kFrecencyFieldTrialLimitParam] = limit_param; - variations::AssociateVariationParams(kFrecencyFieldTrialName, "LimitToN", - params); - - field_trial_ = base::FieldTrialList::CreateFieldTrial( - kFrecencyFieldTrialName, "LimitToN"); - field_trial_->group(); - } - void SubmitFormAndExpectImportedCardWithData(const FormData& form, const char* exp_name, const char* exp_cc_num, @@ -364,8 +348,7 @@ class PersonalDataManagerTest : public testing::Test { PersonalDataLoadedObserverMock personal_data_observer_; std::unique_ptr<PersonalDataManager> personal_data_; - std::unique_ptr<base::FieldTrialList> field_trial_list_; - scoped_refptr<base::FieldTrial> field_trial_; + variations::testing::VariationParamsManager variation_params_; }; TEST_F(PersonalDataManagerTest, AddProfile) { @@ -417,6 +400,10 @@ TEST_F(PersonalDataManagerTest, AddProfile) { // Test that a new profile has its basic information set. TEST_F(PersonalDataManagerTest, AddProfile_BasicInformation) { + // Create the test clock and set the time to a specific value. + TestAutofillClock test_clock; + test_clock.SetNow(kArbitraryTime); + // Add a profile to the database. AutofillProfile profile(test::GetFullProfile()); profile.SetRawInfo(EMAIL_ADDRESS, ASCIIToUTF16("j@s.com")); @@ -432,52 +419,8 @@ TEST_F(PersonalDataManagerTest, AddProfile_BasicInformation) { // Make sure the use count and use date were set. EXPECT_EQ(1U, results[0]->use_count()); - EXPECT_NE(base::Time(), results[0]->use_date()); - EXPECT_NE(base::Time(), results[0]->modification_date()); -} - -TEST_F(PersonalDataManagerTest, DontDuplicateServerProfile) { - EnableWalletCardImport(); - - std::vector<AutofillProfile> server_profiles; - server_profiles.push_back( - AutofillProfile(AutofillProfile::SERVER_PROFILE, "a123")); - test::SetProfileInfo(&server_profiles.back(), "John", "", "Doe", "", - "ACME Corp", "500 Oak View", "Apt 8", "Houston", "TX", - "77401", "US", ""); - // Wallet only provides a full name, so the above first and last names - // will be ignored when the profile is written to the DB. - server_profiles.back().SetRawInfo(NAME_FULL, ASCIIToUTF16("John Doe")); - autofill_table_->SetServerProfiles(server_profiles); - personal_data_->Refresh(); - EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()) - .WillOnce(QuitMainMessageLoop()); - base::RunLoop().Run(); - EXPECT_EQ(1U, personal_data_->GetProfiles().size()); - - // Add profile with identical values. Duplicates should not get saved. - AutofillProfile scraped_profile(base::GenerateGUID(), - "https://www.example.com"); - test::SetProfileInfo(&scraped_profile, "John", "", "Doe", "", "ACME Corp", - "500 Oak View", "Apt 8", "Houston", "TX", "77401", "US", - ""); - EXPECT_TRUE(scraped_profile.IsSubsetOf(server_profiles.back(), "en-US")); - std::string saved_guid = personal_data_->SaveImportedProfile(scraped_profile); - EXPECT_NE(scraped_profile.guid(), saved_guid); - - personal_data_->Refresh(); - EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()) - .WillOnce(QuitMainMessageLoop()); - base::RunLoop().Run(); - - // Verify the non-addition. - EXPECT_EQ(0U, personal_data_->web_profiles().size()); - ASSERT_EQ(1U, personal_data_->GetProfiles().size()); - - // Verify that the server profile's use date was updated. - const AutofillProfile* server_profile = personal_data_->GetProfiles()[0]; - EXPECT_GT(base::TimeDelta::FromMilliseconds(500), - base::Time::Now() - server_profile->use_date()); + EXPECT_EQ(kArbitraryTime, results[0]->use_date()); + EXPECT_EQ(kArbitraryTime, results[0]->modification_date()); } // Tests that SaveImportedProfile sets the modification date on new profiles. @@ -489,7 +432,7 @@ TEST_F(PersonalDataManagerTest, SaveImportedProfileSetModificationDate) { const std::vector<AutofillProfile*>& profiles = personal_data_->GetProfiles(); ASSERT_EQ(1U, profiles.size()); EXPECT_GT(base::TimeDelta::FromMilliseconds(500), - base::Time::Now() - profiles[0]->modification_date()); + AutofillClock::Now() - profiles[0]->modification_date()); } TEST_F(PersonalDataManagerTest, AddUpdateRemoveProfiles) { @@ -607,6 +550,10 @@ TEST_F(PersonalDataManagerTest, AddUpdateRemoveCreditCards) { // Test that a new credit card has its basic information set. TEST_F(PersonalDataManagerTest, AddCreditCard_BasicInformation) { + // Create the test clock and set the time to a specific value. + TestAutofillClock test_clock; + test_clock.SetNow(kArbitraryTime); + // Add a credit to the database. CreditCard credit_card(base::GenerateGUID(), "https://www.example.com"); test::SetCreditCardInfo(&credit_card, "John Dillinger", @@ -623,8 +570,8 @@ TEST_F(PersonalDataManagerTest, AddCreditCard_BasicInformation) { // Make sure the use count and use date were set. EXPECT_EQ(1U, results[0]->use_count()); - EXPECT_NE(base::Time(), results[0]->use_date()); - EXPECT_NE(base::Time(), results[0]->modification_date()); + EXPECT_EQ(kArbitraryTime, results[0]->use_date()); + EXPECT_EQ(kArbitraryTime, results[0]->modification_date()); } TEST_F(PersonalDataManagerTest, UpdateUnverifiedProfilesAndCreditCards) { @@ -3416,7 +3363,7 @@ TEST_F(PersonalDataManagerTest, GetProfileSuggestions_Ranking) { "johnwayne@me.xyz", "Fox", "123 Zoo St.\nSecond Line\nThird line", "unit 5", "Hollywood", "CA", "91601", "US", "12345678910"); - profile3.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(1)); + profile3.set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(1)); profile3.set_use_count(5); personal_data_->AddProfile(profile3); @@ -3425,7 +3372,7 @@ TEST_F(PersonalDataManagerTest, GetProfileSuggestions_Ranking) { "johnwayne@me.xyz", "Fox", "123 Zoo St.\nSecond Line\nThird line", "unit 5", "Hollywood", "CA", "91601", "US", "12345678910"); - profile1.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(1)); + profile1.set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(1)); profile1.set_use_count(10); personal_data_->AddProfile(profile1); @@ -3434,7 +3381,7 @@ TEST_F(PersonalDataManagerTest, GetProfileSuggestions_Ranking) { "johnwayne@me.xyz", "Fox", "123 Zoo St.\nSecond Line\nThird line", "unit 5", "Hollywood", "CA", "91601", "US", "12345678910"); - profile2.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(15)); + profile2.set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(15)); profile2.set_use_count(300); personal_data_->AddProfile(profile2); @@ -3482,7 +3429,8 @@ TEST_F(PersonalDataManagerTest, GetProfileSuggestions_NumberOfSuggestions) { EXPECT_EQ(3U, suggestions.size()); // Verify that only two profiles are suggested. - EnableAutofillProfileLimitFieldTrial("2"); + variation_params_.SetVariationParams(kFrecencyFieldTrialName, + {{kFrecencyFieldTrialLimitParam, "2"}}); suggestions = personal_data_->GetProfileSuggestions( AutofillType(NAME_FIRST), base::string16(), false, @@ -3495,7 +3443,8 @@ TEST_F(PersonalDataManagerTest, GetProfileSuggestions_NumberOfSuggestions) { // than three profiles. TEST_F(PersonalDataManagerTest, GetProfileSuggestions_LimitIsMoreThanProfileSuggestions) { - EnableAutofillProfileLimitFieldTrial("3"); + variation_params_.SetVariationParams(kFrecencyFieldTrialName, + {{kFrecencyFieldTrialLimitParam, "3"}}); // Set up 2 different profiles. AutofillProfile profile1(base::GenerateGUID(), "https://www.example.com"); @@ -3583,7 +3532,7 @@ TEST_F(PersonalDataManagerTest, test::SetCreditCardInfo(&server_cards.back(), "Emmet Dalton", "2110", "12", "2999"); server_cards.back().set_use_count(2); - server_cards.back().set_use_date(base::Time::Now() - + server_cards.back().set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(1)); server_cards.back().SetTypeForMaskedCard(kVisaCard); @@ -3591,7 +3540,7 @@ TEST_F(PersonalDataManagerTest, test::SetCreditCardInfo(&server_cards.back(), "Jesse James", "2109", "12", "2999"); server_cards.back().set_use_count(6); - server_cards.back().set_use_date(base::Time::Now() - + server_cards.back().set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(1)); test::SetServerCreditCards(autofill_table_, server_cards); @@ -3632,14 +3581,16 @@ TEST_F(PersonalDataManagerTest, GetCreditCardSuggestions_ExpiredCards) { test::SetCreditCardInfo(&credit_card1, "Clyde Barrow", "347666888555" /* American Express */, "04", "1999"); credit_card1.set_use_count(300); - credit_card1.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(10)); + credit_card1.set_use_date(AutofillClock::Now() - + base::TimeDelta::FromDays(10)); personal_data_->AddCreditCard(credit_card1); // Add an expired card with a lower frecency score. CreditCard credit_card2("1141084B-72D7-4B73-90CF-3D6AC154673B", "https://www.example.com"); credit_card2.set_use_count(3); - credit_card2.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(1)); + credit_card2.set_use_date(AutofillClock::Now() - + base::TimeDelta::FromDays(1)); test::SetCreditCardInfo(&credit_card2, "John Dillinger", "423456789012" /* Visa */, "01", "1998"); personal_data_->AddCreditCard(credit_card2); @@ -3675,13 +3626,15 @@ TEST_F(PersonalDataManagerTest, GetCreditCardSuggestions_NumberMissing) { test::SetCreditCardInfo(&credit_card0, "Clyde Barrow", "347666888555" /* American Express */, "04", "2999"); credit_card0.set_use_count(3); - credit_card0.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(1)); + credit_card0.set_use_date(AutofillClock::Now() - + base::TimeDelta::FromDays(1)); personal_data_->AddCreditCard(credit_card0); CreditCard credit_card1("1141084B-72D7-4B73-90CF-3D6AC154673B", "https://www.example.com"); credit_card1.set_use_count(300); - credit_card1.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(10)); + credit_card1.set_use_date(AutofillClock::Now() - + base::TimeDelta::FromDays(10)); test::SetCreditCardInfo(&credit_card1, "John Dillinger", "", "01", "2999"); personal_data_->AddCreditCard(credit_card1); @@ -3718,7 +3671,7 @@ TEST_F(PersonalDataManagerTest, GetCreditCardSuggestions_ServerDuplicates) { test::SetCreditCardInfo(&server_cards.back(), "John Dillinger", "9012" /* Visa */, "01", "2999"); server_cards.back().set_use_count(2); - server_cards.back().set_use_date(base::Time::Now() - + server_cards.back().set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(15)); server_cards.back().SetTypeForMaskedCard(kVisaCard); @@ -3728,7 +3681,7 @@ TEST_F(PersonalDataManagerTest, GetCreditCardSuggestions_ServerDuplicates) { test::SetCreditCardInfo(&server_cards.back(), "Bonnie Parker", "2109", "12", "2999"); server_cards.back().set_use_count(3); - server_cards.back().set_use_date(base::Time::Now() - + server_cards.back().set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(15)); server_cards.back().SetTypeForMaskedCard(kVisaCard); @@ -3739,7 +3692,7 @@ TEST_F(PersonalDataManagerTest, GetCreditCardSuggestions_ServerDuplicates) { test::SetCreditCardInfo(&server_cards.back(), "Clyde Barrow", "347666888555" /* American Express */, "04", "2999"); server_cards.back().set_use_count(1); - server_cards.back().set_use_date(base::Time::Now() - + server_cards.back().set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(15)); test::SetServerCreditCards(autofill_table_, server_cards); @@ -3826,7 +3779,7 @@ TEST_F(PersonalDataManagerTest, test::SetCreditCardInfo(&local_card, "Homer Simpson", "423456789012" /* Visa */, "01", "2999"); local_card.set_use_count(3); - local_card.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(1)); + local_card.set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(1)); credit_cards.push_back(&local_card); // Create a full server card that is a duplicate of one of the local cards. @@ -3834,7 +3787,7 @@ TEST_F(PersonalDataManagerTest, test::SetCreditCardInfo(&full_server_card, "Homer Simpson", "423456789012" /* Visa */, "01", "2999"); full_server_card.set_use_count(1); - full_server_card.set_use_date(base::Time::Now() - + full_server_card.set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(15)); credit_cards.push_back(&full_server_card); @@ -3853,7 +3806,7 @@ TEST_F(PersonalDataManagerTest, DedupeCreditCardToSuggest_LocalShadowsMasked) { CreditCard local_card("1141084B-72D7-4B73-90CF-3D6AC154673B", "https://www.example.com"); local_card.set_use_count(300); - local_card.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(10)); + local_card.set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(10)); test::SetCreditCardInfo(&local_card, "Homer Simpson", "423456789012" /* Visa */, "01", "2999"); credit_cards.push_back(&local_card); @@ -3863,7 +3816,8 @@ TEST_F(PersonalDataManagerTest, DedupeCreditCardToSuggest_LocalShadowsMasked) { test::SetCreditCardInfo(&masked_card, "Homer Simpson", "9012" /* Visa */, "01", "2999"); masked_card.set_use_count(2); - masked_card.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(15)); + masked_card.set_use_date(AutofillClock::Now() - + base::TimeDelta::FromDays(15)); masked_card.SetTypeForMaskedCard(kVisaCard); credit_cards.push_back(&masked_card); @@ -3883,7 +3837,7 @@ TEST_F(PersonalDataManagerTest, DedupeCreditCardToSuggest_FullServerAndMasked) { test::SetCreditCardInfo(&full_server_card, "Homer Simpson", "423456789012" /* Visa */, "01", "2999"); full_server_card.set_use_count(1); - full_server_card.set_use_date(base::Time::Now() - + full_server_card.set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(15)); credit_cards.push_back(&full_server_card); @@ -3892,7 +3846,8 @@ TEST_F(PersonalDataManagerTest, DedupeCreditCardToSuggest_FullServerAndMasked) { test::SetCreditCardInfo(&masked_card, "Homer Simpson", "9012" /* Visa */, "01", "2999"); masked_card.set_use_count(2); - masked_card.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(15)); + masked_card.set_use_date(AutofillClock::Now() - + base::TimeDelta::FromDays(15)); masked_card.SetTypeForMaskedCard(kVisaCard); credit_cards.push_back(&masked_card); @@ -3908,7 +3863,8 @@ TEST_F(PersonalDataManagerTest, DedupeCreditCardToSuggest_DifferentCards) { CreditCard credit_card2("002149C1-EE28-4213-A3B9-DA243FFF021B", "https://www.example.com"); credit_card2.set_use_count(1); - credit_card2.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(1)); + credit_card2.set_use_date(AutofillClock::Now() - + base::TimeDelta::FromDays(1)); test::SetCreditCardInfo(&credit_card2, "Homer Simpson", "518765432109" /* Mastercard */, "", ""); credit_cards.push_back(&credit_card2); @@ -3917,7 +3873,8 @@ TEST_F(PersonalDataManagerTest, DedupeCreditCardToSuggest_DifferentCards) { CreditCard credit_card4(CreditCard::MASKED_SERVER_CARD, "b456"); test::SetCreditCardInfo(&credit_card4, "Homer Simpson", "2109", "12", "2999"); credit_card4.set_use_count(3); - credit_card4.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(15)); + credit_card4.set_use_date(AutofillClock::Now() - + base::TimeDelta::FromDays(15)); credit_card4.SetTypeForMaskedCard(kVisaCard); credit_cards.push_back(&credit_card4); @@ -3927,7 +3884,8 @@ TEST_F(PersonalDataManagerTest, DedupeCreditCardToSuggest_DifferentCards) { test::SetCreditCardInfo(&credit_card5, "Homer Simpson", "347666888555" /* American Express */, "04", "2999"); credit_card5.set_use_count(1); - credit_card5.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(15)); + credit_card5.set_use_date(AutofillClock::Now() - + base::TimeDelta::FromDays(15)); credit_cards.push_back(&credit_card5); PersonalDataManager::DedupeCreditCardToSuggest(&credit_cards); @@ -3935,38 +3893,39 @@ TEST_F(PersonalDataManagerTest, DedupeCreditCardToSuggest_DifferentCards) { } TEST_F(PersonalDataManagerTest, RecordUseOf) { - base::Time creation_time = base::Time::FromTimeT(12345); + // Create the test clock and set the time to a specific value. + TestAutofillClock test_clock; + test_clock.SetNow(kArbitraryTime); AutofillProfile profile(test::GetFullProfile()); - profile.set_use_date(creation_time); - profile.set_modification_date(creation_time); EXPECT_EQ(1U, profile.use_count()); - EXPECT_EQ(creation_time, profile.use_date()); - EXPECT_EQ(creation_time, profile.modification_date()); + EXPECT_EQ(kArbitraryTime, profile.use_date()); + EXPECT_EQ(kArbitraryTime, profile.modification_date()); personal_data_->AddProfile(profile); CreditCard credit_card(base::GenerateGUID(), "https://www.example.com"); test::SetCreditCardInfo(&credit_card, "John Dillinger", "423456789012" /* Visa */, "01", "2999"); - credit_card.set_use_date(creation_time); - credit_card.set_modification_date(creation_time); EXPECT_EQ(1U, credit_card.use_count()); - EXPECT_EQ(creation_time, credit_card.use_date()); - EXPECT_EQ(creation_time, credit_card.modification_date()); + EXPECT_EQ(kArbitraryTime, credit_card.use_date()); + EXPECT_EQ(kArbitraryTime, credit_card.modification_date()); personal_data_->AddCreditCard(credit_card); EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()) .WillOnce(QuitMainMessageLoop()); base::RunLoop().Run(); + // Set the current time to another value. + test_clock.SetNow(kSomeLaterTime); + // Notify the PDM that the profile and credit card were used. AutofillProfile* added_profile = personal_data_->GetProfileByGUID(profile.guid()); ASSERT_TRUE(added_profile); EXPECT_EQ(*added_profile, profile); EXPECT_EQ(1U, added_profile->use_count()); - EXPECT_EQ(creation_time, added_profile->use_date()); - EXPECT_NE(creation_time, added_profile->modification_date()); + EXPECT_EQ(kArbitraryTime, added_profile->use_date()); + EXPECT_EQ(kArbitraryTime, added_profile->modification_date()); personal_data_->RecordUseOf(profile); CreditCard* added_card = @@ -3974,8 +3933,8 @@ TEST_F(PersonalDataManagerTest, RecordUseOf) { ASSERT_TRUE(added_card); EXPECT_EQ(*added_card, credit_card); EXPECT_EQ(1U, added_card->use_count()); - EXPECT_EQ(creation_time, added_card->use_date()); - EXPECT_NE(creation_time, added_card->modification_date()); + EXPECT_EQ(kArbitraryTime, added_card->use_date()); + EXPECT_EQ(kArbitraryTime, added_card->modification_date()); personal_data_->RecordUseOf(credit_card); EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()) @@ -3986,14 +3945,14 @@ TEST_F(PersonalDataManagerTest, RecordUseOf) { added_profile = personal_data_->GetProfileByGUID(profile.guid()); ASSERT_TRUE(added_profile); EXPECT_EQ(2U, added_profile->use_count()); - EXPECT_NE(creation_time, added_profile->use_date()); - EXPECT_NE(creation_time, added_profile->modification_date()); + EXPECT_EQ(kSomeLaterTime, added_profile->use_date()); + EXPECT_EQ(kArbitraryTime, added_profile->modification_date()); added_card = personal_data_->GetCreditCardByGUID(credit_card.guid()); ASSERT_TRUE(added_card); EXPECT_EQ(2U, added_card->use_count()); - EXPECT_NE(creation_time, added_card->use_date()); - EXPECT_NE(creation_time, added_card->modification_date()); + EXPECT_EQ(kSomeLaterTime, added_card->use_date()); + EXPECT_EQ(kArbitraryTime, added_card->modification_date()); } TEST_F(PersonalDataManagerTest, UpdateServerCreditCardUsageStats) { @@ -4014,6 +3973,10 @@ TEST_F(PersonalDataManagerTest, UpdateServerCreditCardUsageStats) { test::SetCreditCardInfo(&server_cards.back(), "Clyde Barrow", "347666888555" /* American Express */, "04", "2999"); + // Create the test clock and set the time to a specific value. + TestAutofillClock test_clock; + test_clock.SetNow(kArbitraryTime); + test::SetServerCreditCards(autofill_table_, server_cards); personal_data_->Refresh(); @@ -4052,17 +4015,21 @@ TEST_F(PersonalDataManagerTest, UpdateServerCreditCardUsageStats) { EXPECT_EQ(0, server_cards[i].Compare(*personal_data_->GetCreditCards()[i])); // For an unmasked card, usage data starts out as 2 because of the unmasking - // which is considered a use. + // which is considered a use. The use date should now be the specified Now() + // time kArbitraryTime. EXPECT_EQ(2U, personal_data_->GetCreditCards()[0]->use_count()); - EXPECT_NE(base::Time(), personal_data_->GetCreditCards()[0]->use_date()); + EXPECT_EQ(kArbitraryTime, personal_data_->GetCreditCards()[0]->use_date()); EXPECT_EQ(1U, personal_data_->GetCreditCards()[1]->use_count()); - EXPECT_NE(base::Time(), personal_data_->GetCreditCards()[1]->use_date()); + EXPECT_NE(kArbitraryTime, personal_data_->GetCreditCards()[1]->use_date()); - // Having unmasked this card, usage stats should be 2 and Now(). + // Having unmasked this card, usage stats should be 2 and + // kArbitraryTime. EXPECT_EQ(2U, personal_data_->GetCreditCards()[2]->use_count()); - EXPECT_NE(base::Time(), personal_data_->GetCreditCards()[2]->use_date()); - base::Time initial_use_date = personal_data_->GetCreditCards()[2]->use_date(); + EXPECT_EQ(kArbitraryTime, personal_data_->GetCreditCards()[2]->use_date()); + + // Change the Now() value for a second time. + test_clock.SetNow(kSomeLaterTime); server_cards.back().set_guid(personal_data_->GetCreditCards()[2]->guid()); personal_data_->RecordUseOf(server_cards.back()); @@ -4072,15 +4039,15 @@ TEST_F(PersonalDataManagerTest, UpdateServerCreditCardUsageStats) { ASSERT_EQ(3U, personal_data_->GetCreditCards().size()); EXPECT_EQ(2U, personal_data_->GetCreditCards()[0]->use_count()); - EXPECT_NE(base::Time(), personal_data_->GetCreditCards()[0]->use_date()); + EXPECT_EQ(kArbitraryTime, personal_data_->GetCreditCards()[0]->use_date()); EXPECT_EQ(1U, personal_data_->GetCreditCards()[1]->use_count()); - EXPECT_NE(base::Time(), personal_data_->GetCreditCards()[1]->use_date()); + EXPECT_NE(kArbitraryTime, personal_data_->GetCreditCards()[1]->use_date()); + // The RecordUseOf call should have incremented the use_count to 3 and set the + // use_date to kSomeLaterTime. EXPECT_EQ(3U, personal_data_->GetCreditCards()[2]->use_count()); - EXPECT_NE(base::Time(), personal_data_->GetCreditCards()[2]->use_date()); - // Time may or may not have elapsed between unmasking and RecordUseOf. - EXPECT_LE(initial_use_date, personal_data_->GetCreditCards()[2]->use_date()); + EXPECT_EQ(kSomeLaterTime, personal_data_->GetCreditCards()[2]->use_date()); // Can record usage stats on masked cards. server_cards[1].set_guid(personal_data_->GetCreditCards()[1]->guid()); @@ -4090,7 +4057,10 @@ TEST_F(PersonalDataManagerTest, UpdateServerCreditCardUsageStats) { base::RunLoop().Run(); ASSERT_EQ(3U, personal_data_->GetCreditCards().size()); EXPECT_EQ(2U, personal_data_->GetCreditCards()[1]->use_count()); - EXPECT_NE(base::Time(), personal_data_->GetCreditCards()[1]->use_date()); + EXPECT_EQ(kSomeLaterTime, personal_data_->GetCreditCards()[1]->use_date()); + + // Change Now()'s return value for a third time. + test_clock.SetNow(kMuchLaterTime); // Upgrading to unmasked retains the usage stats (and increments them). CreditCard* unmasked_card2 = &server_cards[1]; @@ -4105,7 +4075,7 @@ TEST_F(PersonalDataManagerTest, UpdateServerCreditCardUsageStats) { base::RunLoop().Run(); ASSERT_EQ(3U, personal_data_->GetCreditCards().size()); EXPECT_EQ(3U, personal_data_->GetCreditCards()[1]->use_count()); - EXPECT_NE(base::Time(), personal_data_->GetCreditCards()[1]->use_date()); + EXPECT_EQ(kMuchLaterTime, personal_data_->GetCreditCards()[1]->use_date()); } TEST_F(PersonalDataManagerTest, ClearAllServerData) { @@ -4121,26 +4091,15 @@ TEST_F(PersonalDataManagerTest, ClearAllServerData) { // Need to set the google services username EnableWalletCardImport(); - // Add a server profile. - std::vector<AutofillProfile> server_profiles; - server_profiles.push_back( - AutofillProfile(AutofillProfile::SERVER_PROFILE, "a123")); - test::SetProfileInfo(&server_profiles.back(), "John", "", "Doe", "", - "ACME Corp", "500 Oak View", "Apt 8", "Houston", "TX", - "77401", "US", ""); - autofill_table_->SetServerProfiles(server_profiles); - // The card and profile should be there. ResetPersonalDataManager(USER_MODE_NORMAL); EXPECT_FALSE(personal_data_->GetCreditCards().empty()); - EXPECT_FALSE(personal_data_->GetProfiles().empty()); personal_data_->ClearAllServerData(); // Reload the database, everything should be gone. ResetPersonalDataManager(USER_MODE_NORMAL); EXPECT_TRUE(personal_data_->GetCreditCards().empty()); - EXPECT_TRUE(personal_data_->GetProfiles().empty()); } TEST_F(PersonalDataManagerTest, DontDuplicateServerCard) { @@ -4473,7 +4432,13 @@ TEST_F(PersonalDataManagerTest, SaveImportedProfile) { {{COMPANY_NAME, "Stark Inc."}}}, }; + // Create the test clock. + TestAutofillClock test_clock; + for (TestCase test_case : test_cases) { + // Set the time to a specific value. + test_clock.SetNow(kArbitraryTime); + SetupReferenceProfile(); const std::vector<AutofillProfile*>& initial_profiles = personal_data_->GetProfiles(); @@ -4484,6 +4449,9 @@ TEST_F(PersonalDataManagerTest, SaveImportedProfile) { change.field_type, base::UTF8ToUTF16(change.field_value)); } + // Set the time to a bigger value. + test_clock.SetNow(kSomeLaterTime); + AutofillProfile profile2(base::GenerateGUID(), "https://www.example.com"); test::SetProfileInfo(&profile2, "Marion", "Mitchell", "Morrison", "johnwayne@me.xyz", "Fox", "123 Zoo St", "unit 5", @@ -4515,11 +4483,8 @@ TEST_F(PersonalDataManagerTest, SaveImportedProfile) { // Verify that the merged profile's use count, use date and modification // date were properly updated. EXPECT_EQ(1U, saved_profiles.front()->use_count()); - EXPECT_GT(base::TimeDelta::FromMilliseconds(500), - base::Time::Now() - saved_profiles.front()->use_date()); - EXPECT_GT( - base::TimeDelta::FromMilliseconds(500), - base::Time::Now() - saved_profiles.front()->modification_date()); + EXPECT_EQ(kSomeLaterTime, saved_profiles.front()->use_date()); + EXPECT_EQ(kSomeLaterTime, saved_profiles.front()->modification_date()); } // Erase the profiles for the next test. @@ -4568,7 +4533,17 @@ TEST_F(PersonalDataManagerTest, MergeProfile_Frecency) { // Tests that MergeProfile produces a merged profile with the expected usage // statistics. -TEST_F(PersonalDataManagerTest, MergeProfile_UsageStats) { +// Flaky on TSan, see crbug.com/686226. +#if defined(THREAD_SANITIZER) +#define MAYBE_MergeProfile_UsageStats DISABLED_MergeProfile_UsageStats +#else +#define MAYBE_MergeProfile_UsageStats MergeProfile_UsageStats +#endif +TEST_F(PersonalDataManagerTest, MAYBE_MergeProfile_UsageStats) { + // Create the test clock and set the time to a specific value. + TestAutofillClock test_clock; + test_clock.SetNow(kArbitraryTime); + // Create an initial profile with a use count of 10, an old use date and an // old modification date of 4 days ago. AutofillProfile* profile = @@ -4577,14 +4552,16 @@ TEST_F(PersonalDataManagerTest, MergeProfile_UsageStats) { "homer.simpson@abc.com", "SNP", "742 Evergreen Terrace", "", "Springfield", "IL", "91601", "US", "12345678910"); profile->set_use_count(4U); - profile->set_use_date(base::Time::Now() - base::TimeDelta::FromDays(4)); - profile->set_modification_date(base::Time::Now() - - base::TimeDelta::FromDays(4)); + EXPECT_EQ(kArbitraryTime, profile->use_date()); + EXPECT_EQ(kArbitraryTime, profile->modification_date()); // Create the |existing_profiles| vector. std::vector<std::unique_ptr<AutofillProfile>> existing_profiles; existing_profiles.push_back(base::WrapUnique(profile)); + // Change the current date. + test_clock.SetNow(kSomeLaterTime); + // Create a new imported profile that will get merged with the existing one. AutofillProfile imported_profile(base::GenerateGUID(), "https://www.example.com"); @@ -4592,6 +4569,9 @@ TEST_F(PersonalDataManagerTest, MergeProfile_UsageStats) { "homer.simpson@abc.com", "", "742 Evergreen Terrace", "", "Springfield", "IL", "91601", "US", "12345678910"); + // Change the current date. + test_clock.SetNow(kMuchLaterTime); + // Merge the imported profile into the existing profiles. std::vector<AutofillProfile> profiles; std::string guid = personal_data_->MergeProfile( @@ -4601,12 +4581,12 @@ TEST_F(PersonalDataManagerTest, MergeProfile_UsageStats) { EXPECT_EQ(profile->guid(), guid); // The use count should have be max(4, 1) => 4. EXPECT_EQ(4U, profile->use_count()); - // The use date and modification dates should have been set to less than 500 - // milliseconds ago. - EXPECT_GT(base::TimeDelta::FromMilliseconds(500), - base::Time::Now() - profile->use_date()); - EXPECT_GT(base::TimeDelta::FromMilliseconds(500), - base::Time::Now() - profile->modification_date()); + // The use date should be the one of the most recent profile, which is + // kSecondArbitraryTime. + EXPECT_EQ(kSomeLaterTime, profile->use_date()); + // Since the merge is considered a modification, the modification_date should + // be set to kMuchLaterTime. + EXPECT_EQ(kMuchLaterTime, profile->modification_date()); } // Tests that DedupeProfiles sets the correct profile guids to @@ -4803,9 +4783,11 @@ TEST_F(PersonalDataManagerTest, UpdateCardsBillingAddressReference) { // 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_->server_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_->server_credit_cards_.push_back( + base::WrapUnique(credit_card4)); personal_data_->UpdateCardsBillingAddressReference(guids_merge_map); @@ -4838,7 +4820,7 @@ TEST_F(PersonalDataManagerTest, "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)); + profile1.set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(1)); // Create a profile with a medium frecency score. AutofillProfile profile2(base::GenerateGUID(), "https://www.example.com"); @@ -4846,7 +4828,7 @@ TEST_F(PersonalDataManagerTest, "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)); + profile2.set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(3)); // Create a profile with a lower frecency score. AutofillProfile profile3(base::GenerateGUID(), "https://www.example.com"); @@ -4854,7 +4836,7 @@ TEST_F(PersonalDataManagerTest, "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)); + profile3.set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(5)); // Create a set of two profiles to be merged together. // Create a profile with a higher frecency score. @@ -4863,7 +4845,7 @@ TEST_F(PersonalDataManagerTest, "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)); + profile4.set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(1)); // Create a profile with a lower frecency score. AutofillProfile profile5(base::GenerateGUID(), "https://www.example.com"); @@ -4871,7 +4853,7 @@ TEST_F(PersonalDataManagerTest, "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)); + profile5.set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(3)); // Create a unique profile. AutofillProfile profile6(base::GenerateGUID(), "https://www.example.com"); @@ -4879,7 +4861,7 @@ TEST_F(PersonalDataManagerTest, "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)); + profile6.set_use_date(AutofillClock::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 @@ -4970,7 +4952,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MergedProfileValues) { "homer.simpson@abc.com", "", "742. Evergreen Terrace", "", "Springfield", "IL", "91601", "US", ""); profile1.set_use_count(10); - profile1.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(1)); + profile1.set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(1)); // Create a profile with a medium frecency score. AutofillProfile profile2(base::GenerateGUID(), "https://www.example.com"); @@ -4978,7 +4960,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MergedProfileValues) { "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)); + profile2.set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(3)); // Create a profile with a lower frecency score. AutofillProfile profile3(base::GenerateGUID(), "https://www.example.com"); @@ -4986,7 +4968,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MergedProfileValues) { "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)); + profile3.set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(5)); personal_data_->AddProfile(profile1); personal_data_->AddProfile(profile2); @@ -5062,7 +5044,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_VerifiedProfileFirst) { "homer.simpson@abc.com", "", "742 Evergreen Terrace", "", "Springfield", "IL", "91601", "", "12345678910"); profile1.set_use_count(7); - profile1.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(1)); + profile1.set_use_date(kMuchLaterTime); // Create a similar non verified profile with a medium frecency score. AutofillProfile profile2(base::GenerateGUID(), "https://www.example.com"); @@ -5070,7 +5052,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_VerifiedProfileFirst) { "homer.simpson@abc.com", "", "742. Evergreen Terrace", "", "Springfield", "IL", "91601", "US", ""); profile2.set_use_count(5); - profile2.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(3)); + profile2.set_use_date(kSomeLaterTime); // Create a similar non verified profile with a lower frecency score. AutofillProfile profile3(base::GenerateGUID(), "https://www.example.com"); @@ -5078,7 +5060,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_VerifiedProfileFirst) { "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)); + profile3.set_use_date(kArbitraryTime); personal_data_->AddProfile(profile1); personal_data_->AddProfile(profile2); @@ -5118,10 +5100,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_VerifiedProfileFirst) { EXPECT_EQ(profile1.guid(), profiles[0]->guid()); EXPECT_TRUE(profile1 == *profiles[0]); EXPECT_EQ(profile1.use_count(), profiles[0]->use_count()); - EXPECT_LT(profile1.use_date() - TimeDelta::FromSeconds(2), - profiles[0]->use_date()); - EXPECT_GT(profile1.use_date() + TimeDelta::FromSeconds(2), - profiles[0]->use_date()); + EXPECT_EQ(profile1.use_date(), profiles[0]->use_date()); } // Tests that ApplyDedupingRoutine only keeps the verified profile with its @@ -5134,7 +5113,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_VerifiedProfileLast) { "homer.simpson@abc.com", "", "742. Evergreen Terrace", "", "Springfield", "IL", "91601", "US", ""); profile1.set_use_count(5); - profile1.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(3)); + profile1.set_use_date(kMuchLaterTime); // Create a similar non verified profile with a medium frecency score. AutofillProfile profile2(base::GenerateGUID(), "https://www.example.com"); @@ -5142,7 +5121,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_VerifiedProfileLast) { "homer.simpson@abc.com", "Fox", "742 Evergreen Terrace.", "", "Springfield", "IL", "91601", "", ""); profile2.set_use_count(5); - profile2.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(3)); + profile2.set_use_date(kSomeLaterTime); // Create a similar verified profile with a lower frecency score. AutofillProfile profile3(base::GenerateGUID(), kSettingsOrigin); @@ -5150,7 +5129,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_VerifiedProfileLast) { "homer.simpson@abc.com", "", "742 Evergreen Terrace", "", "Springfield", "IL", "91601", "", "12345678910"); profile3.set_use_count(3); - profile3.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(5)); + profile3.set_use_date(kArbitraryTime); personal_data_->AddProfile(profile1); personal_data_->AddProfile(profile2); @@ -5186,14 +5165,11 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_VerifiedProfileLast) { histogram_tester.ExpectUniqueSample( "Autofill.NumberOfProfilesRemovedDuringDedupe", 2, 1); - // Only the verified |profile2| with it's original data should have been kept. + // Only the verified |profile3| with it's original data should have been kept. EXPECT_EQ(profile3.guid(), profiles[0]->guid()); EXPECT_TRUE(profile3 == *profiles[0]); EXPECT_EQ(profile3.use_count(), profiles[0]->use_count()); - EXPECT_LT(profile3.use_date() - TimeDelta::FromSeconds(2), - profiles[0]->use_date()); - EXPECT_GT(profile3.use_date() + TimeDelta::FromSeconds(2), - profiles[0]->use_date()); + EXPECT_EQ(profile3.use_date(), profiles[0]->use_date()); } // Tests that ApplyDedupingRoutine does not merge unverified data into @@ -5205,7 +5181,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MultipleVerifiedProfiles) { "homer.simpson@abc.com", "", "742. Evergreen Terrace", "", "Springfield", "IL", "91601", "US", ""); profile1.set_use_count(5); - profile1.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(3)); + profile1.set_use_date(kMuchLaterTime); // Create a similar verified profile with a medium frecency score. AutofillProfile profile2(base::GenerateGUID(), kSettingsOrigin); @@ -5213,7 +5189,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MultipleVerifiedProfiles) { "homer.simpson@abc.com", "Fox", "742 Evergreen Terrace.", "", "Springfield", "IL", "91601", "", ""); profile2.set_use_count(5); - profile2.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(3)); + profile2.set_use_date(kSomeLaterTime); // Create a similar verified profile with a lower frecency score. AutofillProfile profile3(base::GenerateGUID(), kSettingsOrigin); @@ -5221,7 +5197,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MultipleVerifiedProfiles) { "homer.simpson@abc.com", "", "742 Evergreen Terrace", "", "Springfield", "IL", "91601", "", "12345678910"); profile3.set_use_count(3); - profile3.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(5)); + profile3.set_use_date(kArbitraryTime); personal_data_->AddProfile(profile1); personal_data_->AddProfile(profile2); @@ -5268,14 +5244,8 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MultipleVerifiedProfiles) { EXPECT_TRUE(profile3 == *profiles[1]); EXPECT_EQ(profile2.use_count(), profiles[0]->use_count()); EXPECT_EQ(profile3.use_count(), profiles[1]->use_count()); - EXPECT_LT(profile2.use_date() - TimeDelta::FromSeconds(2), - profiles[0]->use_date()); - EXPECT_GT(profile2.use_date() + TimeDelta::FromSeconds(2), - profiles[0]->use_date()); - EXPECT_LT(profile3.use_date() - TimeDelta::FromSeconds(2), - profiles[1]->use_date()); - EXPECT_GT(profile3.use_date() + TimeDelta::FromSeconds(2), - profiles[1]->use_date()); + EXPECT_EQ(profile2.use_date(), profiles[0]->use_date()); + EXPECT_EQ(profile3.use_date(), profiles[1]->use_date()); } // Tests that ApplyProfileUseDatesFix sets the use date of profiles from an @@ -5292,6 +5262,7 @@ TEST_F(PersonalDataManagerTest, ApplyProfileUseDatesFix) { test::SetProfileInfo(&profile1, "Homer", "Jay", "Simpson", "homer.simpson@abc.com", "SNP", "742 Evergreen Terrace", "", "Springfield", "IL", "91601", "US", "12345678910"); + profile1.set_use_date(kArbitraryTime); // Create another profile and set its use date to the default value. AutofillProfile profile2(base::GenerateGUID(), "https://www.example.com"); @@ -5314,14 +5285,17 @@ TEST_F(PersonalDataManagerTest, ApplyProfileUseDatesFix) { ASSERT_EQ(2U, saved_profiles.size()); // The use dates should not have been modified. - EXPECT_LE(base::Time::Now() - base::TimeDelta::FromDays(1), - saved_profiles[0]->use_date()); - EXPECT_EQ(base::Time(), saved_profiles[1]->use_date()); + EXPECT_EQ(profile1.use_date(), saved_profiles[0]->use_date()); + EXPECT_EQ(profile2.use_date(), saved_profiles[1]->use_date()); // Set the pref to false to indicate the fix has never been run. personal_data_->pref_service_->SetBoolean( prefs::kAutofillProfileUseDatesFixed, false); + // Create the test clock and set the time to a specific value. + TestAutofillClock test_clock; + test_clock.SetNow(kSomeLaterTime); + personal_data_->ApplyProfileUseDatesFix(); EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()) .WillOnce(QuitMainMessageLoop()); @@ -5333,12 +5307,9 @@ TEST_F(PersonalDataManagerTest, ApplyProfileUseDatesFix) { ASSERT_EQ(2U, saved_profiles.size()); // |profile1|'s use date should not have been modified. - EXPECT_LE(base::Time::Now() - base::TimeDelta::FromDays(1), - saved_profiles[0]->use_date()); + EXPECT_LE(profile1.use_date(), saved_profiles[0]->use_date()); // |profile2|'s use date should have been set to two weeks before now. - EXPECT_LE(base::Time::Now() - base::TimeDelta::FromDays(15), - saved_profiles[1]->use_date()); - EXPECT_GE(base::Time::Now() - base::TimeDelta::FromDays(13), + EXPECT_EQ(kSomeLaterTime - base::TimeDelta::FromDays(14), saved_profiles[1]->use_date()); } @@ -5355,6 +5326,7 @@ TEST_F(PersonalDataManagerTest, ApplyProfileUseDatesFix_NotAppliedTwice) { test::SetProfileInfo(&profile1, "Homer", "Jay", "Simpson", "homer.simpson@abc.com", "SNP", "742 Evergreen Terrace", "", "Springfield", "IL", "91601", "US", "12345678910"); + profile1.set_use_date(kArbitraryTime); AutofillProfile profile2(base::GenerateGUID(), "https://www.example.com"); test::SetProfileInfo(&profile2, "Marge", "", "Simpson", "homer.simpson@abc.com", "SNP", "742 Evergreen Terrace", @@ -5375,8 +5347,7 @@ TEST_F(PersonalDataManagerTest, ApplyProfileUseDatesFix_NotAppliedTwice) { ASSERT_EQ(2U, saved_profiles.size()); // The use dates should not have been modified. - EXPECT_LE(base::Time::Now() - base::TimeDelta::FromDays(1), - saved_profiles[0]->use_date()); + EXPECT_EQ(profile1.use_date(), saved_profiles[0]->use_date()); EXPECT_EQ(base::Time(), saved_profiles[1]->use_date()); } @@ -5392,7 +5363,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MultipleDedupes) { "homer.simpson@abc.com", "", "742. Evergreen Terrace", "", "Springfield", "IL", "91601", "US", ""); Homer1.set_use_count(10); - Homer1.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(1)); + Homer1.set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(1)); // Create a Homer home profile with a medium frecency score compared to other // Homer profiles. @@ -5401,7 +5372,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MultipleDedupes) { "homer.simpson@abc.com", "", "742 Evergreen Terrace", "", "Springfield", "IL", "91601", "", "12345678910"); Homer2.set_use_count(5); - Homer2.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(3)); + Homer2.set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(3)); // Create a Homer home profile with a lower frecency score than other Homer // profiles. @@ -5410,7 +5381,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MultipleDedupes) { "homer.simpson@abc.com", "Fox", "742 Evergreen Terrace.", "", "Springfield", "IL", "91601", "", ""); Homer3.set_use_count(3); - Homer3.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(5)); + Homer3.set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(5)); // Create a Homer work profile (different address). AutofillProfile Homer4(base::GenerateGUID(), "https://www.example.com"); @@ -5418,7 +5389,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MultipleDedupes) { "homer.simpson@abc.com", "Fox", "12 Nuclear Plant.", "", "Springfield", "IL", "91601", "US", "9876543"); Homer4.set_use_count(3); - Homer4.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(5)); + Homer4.set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(5)); // Create a Marge profile with a lower frecency score that other Marge // profiles. @@ -5427,7 +5398,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MultipleDedupes) { "marge.simpson@abc.com", "", "742 Evergreen Terrace", "", "Springfield", "IL", "91601", "", "12345678910"); Marge1.set_use_count(4); - Marge1.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(3)); + Marge1.set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(3)); // Create a verified Marge home profile with a lower frecency score that the // other Marge profile. @@ -5436,7 +5407,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MultipleDedupes) { "marge.simpson@abc.com", "", "742 Evergreen Terrace", "", "Springfield", "IL", "91601", "", "12345678910"); Marge2.set_use_count(2); - Marge2.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(3)); + Marge2.set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(3)); // Create a Barney profile (guest user). AutofillProfile Barney(base::GenerateGUID(), "https://www.example.com"); @@ -5444,7 +5415,7 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_MultipleDedupes) { "ABC", "123 Other Street", "", "Springfield", "IL", "91601", "", ""); Barney.set_use_count(1); - Barney.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(180)); + Barney.set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(180)); personal_data_->AddProfile(Homer1); personal_data_->AddProfile(Homer2); @@ -5645,4 +5616,509 @@ TEST_F(PersonalDataManagerTest, ApplyDedupingRoutine_OncePerVersion) { EXPECT_EQ(2U, personal_data_->GetProfiles().size()); } +// Tests that a new local profile is created if no existing one is a duplicate +// of the server address. Also tests that the billing address relationship was +// transferred to the converted address. +TEST_F(PersonalDataManagerTest, + ConvertWalletAddressesAndUpdateWalletCards_NewProfile) { + /////////////////////////////////////////////////////////////////////// + // Setup. + /////////////////////////////////////////////////////////////////////// + EnableWalletCardImport(); + base::HistogramTester histogram_tester; + const std::string kServerAddressId("server_address1"); + + // Add two different profiles, a local and a server one. Set the use stats so + // the server profile has a higher frecency, to have a predictable ordering to + // validate results. + AutofillProfile local_profile(base::GenerateGUID(), + "https://www.example.com"); + test::SetProfileInfo(&local_profile, "Josephine", "Alicia", "Saenz", + "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", + "Orlando", "FL", "32801", "US", "19482937549"); + local_profile.set_use_count(1); + personal_data_->AddProfile(local_profile); + + // Add a different server profile. + std::vector<AutofillProfile> GetServerProfiles; + GetServerProfiles.push_back( + AutofillProfile(AutofillProfile::SERVER_PROFILE, kServerAddressId)); + test::SetProfileInfo(&GetServerProfiles.back(), "John", "", "Doe", "", + "ACME Corp", "500 Oak View", "Apt 8", "Houston", "TX", + "77401", "US", ""); + // Wallet only provides a full name, so the above first and last names + // will be ignored when the profile is written to the DB. + GetServerProfiles.back().SetRawInfo(NAME_FULL, ASCIIToUTF16("John Doe")); + GetServerProfiles.back().set_use_count(100); + autofill_table_->SetServerProfiles(GetServerProfiles); + + // Add a server and a local card that have the server address as billing + // address. + CreditCard local_card("287151C8-6AB1-487C-9095-28E80BE5DA15", + "https://www.example.com"); + test::SetCreditCardInfo(&local_card, "Clyde Barrow", + "347666888555" /* American Express */, "04", "2999"); + local_card.set_billing_address_id(kServerAddressId); + personal_data_->AddCreditCard(local_card); + + std::vector<CreditCard> server_cards; + server_cards.push_back( + CreditCard(CreditCard::MASKED_SERVER_CARD, "server_card1")); + test::SetCreditCardInfo(&server_cards.back(), "John Dillinger", + "1111" /* Visa */, "01", "2999"); + server_cards.back().SetTypeForMaskedCard(kVisaCard); + server_cards.back().set_billing_address_id(kServerAddressId); + test::SetServerCreditCards(autofill_table_, server_cards); + + // Make sure everything is setup correctly. + personal_data_->Refresh(); + EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()) + .WillOnce(QuitMainMessageLoop()); + base::RunLoop().Run(); + EXPECT_EQ(1U, personal_data_->web_profiles().size()); + EXPECT_EQ(1U, personal_data_->GetServerProfiles().size()); + EXPECT_EQ(2U, personal_data_->GetCreditCards().size()); + + /////////////////////////////////////////////////////////////////////// + // Tested method. + /////////////////////////////////////////////////////////////////////// + personal_data_->ConvertWalletAddressesAndUpdateWalletCards(); + + /////////////////////////////////////////////////////////////////////// + // Validation. + /////////////////////////////////////////////////////////////////////// + EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()) + .WillOnce(QuitMainMessageLoop()); + base::RunLoop().Run(); + + // The Wallet address should have been added as a new local profile. + EXPECT_EQ(2U, personal_data_->web_profiles().size()); + EXPECT_EQ(1U, personal_data_->GetServerProfiles().size()); + histogram_tester.ExpectUniqueSample("Autofill.WalletAddressConversionType", + AutofillMetrics::CONVERTED_ADDRESS_ADDED, + 1); + + // The conversion should be recorded in the Wallet address. + EXPECT_TRUE(personal_data_->GetServerProfiles().back()->has_converted()); + + // Get the profiles, sorted by frecency to have a deterministic order. + std::vector<AutofillProfile*> profiles = + personal_data_->GetProfilesToSuggest(); + + // Make sure that the two profiles have not merged. + ASSERT_EQ(2U, profiles.size()); + EXPECT_EQ(UTF8ToUTF16("John"), profiles[0]->GetRawInfo(NAME_FIRST)); + EXPECT_EQ(local_profile, *profiles[1]); + + // Make sure that the billing address id of the two cards now point to the + // converted profile. + EXPECT_EQ(profiles[0]->guid(), + personal_data_->GetCreditCards()[0]->billing_address_id()); + EXPECT_EQ(profiles[0]->guid(), + personal_data_->GetCreditCards()[1]->billing_address_id()); + + // Make sure that the added address has the email address of the currently + // signed-in user. + EXPECT_EQ(UTF8ToUTF16("syncuser@example.com"), + profiles[0]->GetRawInfo(EMAIL_ADDRESS)); +} + +// Tests that the converted wallet address is merged into an existing local +// profile if they are considered equivalent. Also tests that the billing +// address relationship was transferred to the converted address. +TEST_F(PersonalDataManagerTest, + ConvertWalletAddressesAndUpdateWalletCards_MergedProfile) { + /////////////////////////////////////////////////////////////////////// + // Setup. + /////////////////////////////////////////////////////////////////////// + EnableWalletCardImport(); + base::HistogramTester histogram_tester; + const std::string kServerAddressId("server_address1"); + + // Add two similar profile, a local and a server one. Set the use stats so + // the server card has a higher frecency, to have a predicatble ordering to + // validate results. + // Add a local profile. + AutofillProfile local_profile(base::GenerateGUID(), + "https://www.example.com"); + test::SetProfileInfo(&local_profile, "John", "", "Doe", "john@doe.com", "", + "1212 Center.", "Bld. 5", "Orlando", "FL", "32801", "US", + "19482937549"); + local_profile.set_use_count(1); + personal_data_->AddProfile(local_profile); + + // Add a different server profile. + std::vector<AutofillProfile> GetServerProfiles; + GetServerProfiles.push_back( + AutofillProfile(AutofillProfile::SERVER_PROFILE, kServerAddressId)); + test::SetProfileInfo(&GetServerProfiles.back(), "John", "", "Doe", "", "Fox", + "1212 Center", "Bld. 5", "Orlando", "FL", "", "US", ""); + // Wallet only provides a full name, so the above first and last names + // will be ignored when the profile is written to the DB. + GetServerProfiles.back().SetRawInfo(NAME_FULL, ASCIIToUTF16("John Doe")); + GetServerProfiles.back().set_use_count(100); + autofill_table_->SetServerProfiles(GetServerProfiles); + + // Add a server and a local card that have the server address as billing + // address. + CreditCard local_card("287151C8-6AB1-487C-9095-28E80BE5DA15", + "https://www.example.com"); + test::SetCreditCardInfo(&local_card, "Clyde Barrow", + "347666888555" /* American Express */, "04", "2999"); + local_card.set_billing_address_id(kServerAddressId); + personal_data_->AddCreditCard(local_card); + + std::vector<CreditCard> server_cards; + server_cards.push_back( + CreditCard(CreditCard::MASKED_SERVER_CARD, "server_card1")); + test::SetCreditCardInfo(&server_cards.back(), "John Dillinger", + "1111" /* Visa */, "01", "2999"); + server_cards.back().SetTypeForMaskedCard(kVisaCard); + server_cards.back().set_billing_address_id(kServerAddressId); + test::SetServerCreditCards(autofill_table_, server_cards); + + // Make sure everything is setup correctly. + personal_data_->Refresh(); + EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()) + .WillOnce(QuitMainMessageLoop()); + base::RunLoop().Run(); + EXPECT_EQ(1U, personal_data_->web_profiles().size()); + EXPECT_EQ(1U, personal_data_->GetServerProfiles().size()); + EXPECT_EQ(2U, personal_data_->GetCreditCards().size()); + + /////////////////////////////////////////////////////////////////////// + // Tested method. + /////////////////////////////////////////////////////////////////////// + personal_data_->ConvertWalletAddressesAndUpdateWalletCards(); + + /////////////////////////////////////////////////////////////////////// + // Validation. + /////////////////////////////////////////////////////////////////////// + EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()) + .WillOnce(QuitMainMessageLoop()); + base::RunLoop().Run(); + + // The Wallet address should have been merged with the existing local profile. + EXPECT_EQ(1U, personal_data_->web_profiles().size()); + EXPECT_EQ(1U, personal_data_->GetServerProfiles().size()); + histogram_tester.ExpectUniqueSample("Autofill.WalletAddressConversionType", + AutofillMetrics::CONVERTED_ADDRESS_MERGED, + 1); + + // The conversion should be recorded in the Wallet address. + EXPECT_TRUE(personal_data_->GetServerProfiles().back()->has_converted()); + + // Get the profiles, sorted by frecency to have a deterministic order. + std::vector<AutofillProfile*> profiles = + personal_data_->GetProfilesToSuggest(); + + // Make sure that the two profiles have merged. + ASSERT_EQ(1U, profiles.size()); + + // Check that the values were merged. + EXPECT_EQ(UTF8ToUTF16("john@doe.com"), + profiles[0]->GetRawInfo(EMAIL_ADDRESS)); + EXPECT_EQ(UTF8ToUTF16("Fox"), profiles[0]->GetRawInfo(COMPANY_NAME)); + EXPECT_EQ(UTF8ToUTF16("32801"), profiles[0]->GetRawInfo(ADDRESS_HOME_ZIP)); + + // Make sure that the billing address id of the two cards now point to the + // converted profile. + EXPECT_EQ(profiles[0]->guid(), + personal_data_->GetCreditCards()[0]->billing_address_id()); + EXPECT_EQ(profiles[0]->guid(), + personal_data_->GetCreditCards()[1]->billing_address_id()); +} + +// Tests that a Wallet address that has already converted does not get converted +// a second time. +TEST_F(PersonalDataManagerTest, + ConvertWalletAddressesAndUpdateWalletCards_AlreadyConverted) { + /////////////////////////////////////////////////////////////////////// + // Setup. + /////////////////////////////////////////////////////////////////////// + EnableWalletCardImport(); + base::HistogramTester histogram_tester; + const std::string kServerAddressId("server_address1"); + + // Add a server profile that has already been converted. + std::vector<AutofillProfile> GetServerProfiles; + GetServerProfiles.push_back( + AutofillProfile(AutofillProfile::SERVER_PROFILE, kServerAddressId)); + test::SetProfileInfo(&GetServerProfiles.back(), "John", "Ray", "Doe", + "john@doe.com", "Fox", "1212 Center", "Bld. 5", + "Orlando", "FL", "32801", "US", ""); + GetServerProfiles.back().set_has_converted(true); + // Wallet only provides a full name, so the above first and last names + // will be ignored when the profile is written to the DB. + autofill_table_->SetServerProfiles(GetServerProfiles); + + // Make sure everything is setup correctly. + personal_data_->Refresh(); + EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()) + .WillOnce(QuitMainMessageLoop()); + base::RunLoop().Run(); + EXPECT_EQ(0U, personal_data_->web_profiles().size()); + EXPECT_EQ(1U, personal_data_->GetServerProfiles().size()); + + /////////////////////////////////////////////////////////////////////// + // Tested method. + /////////////////////////////////////////////////////////////////////// + personal_data_->ConvertWalletAddressesAndUpdateWalletCards(); + + /////////////////////////////////////////////////////////////////////// + // Validation. + /////////////////////////////////////////////////////////////////////// + // Since there should be no change in data, OnPersonalDataChanged should not + // have been called. + EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()).Times(0); + + personal_data_->Refresh(); + EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()) + .WillOnce(QuitMainMessageLoop()); + base::RunLoop().Run(); + + // There should be no local profiles added. + EXPECT_EQ(0U, personal_data_->web_profiles().size()); + EXPECT_EQ(1U, personal_data_->GetServerProfiles().size()); +} + +// Tests that when the user has multiple similar Wallet addresses, they get +// merged into a single local profile, and that the billing address relationship +// is merged too. +TEST_F( + PersonalDataManagerTest, + ConvertWalletAddressesAndUpdateWalletCards_MultipleSimilarWalletAddresses) { + /////////////////////////////////////////////////////////////////////// + // Setup. + /////////////////////////////////////////////////////////////////////// + EnableWalletCardImport(); + base::HistogramTester histogram_tester; + const std::string kServerAddressId("server_address1"); + const std::string kServerAddressId2("server_address2"); + + // Add a unique local profile and two similar server profiles. Set the use + // stats to have a predicatble ordering to validate results. + // Add a local profile. + AutofillProfile local_profile(base::GenerateGUID(), + "https://www.example.com"); + test::SetProfileInfo(&local_profile, "Bob", "", "Doe", "", "Fox", + "1212 Center.", "Bld. 5", "Orlando", "FL", "32801", "US", + "19482937549"); + local_profile.set_use_count(1); + personal_data_->AddProfile(local_profile); + + // Add a server profile. + std::vector<AutofillProfile> GetServerProfiles; + GetServerProfiles.push_back( + AutofillProfile(AutofillProfile::SERVER_PROFILE, kServerAddressId)); + test::SetProfileInfo(&GetServerProfiles.back(), "John", "", "Doe", "", "", + "1212 Center", "Bld. 5", "Orlando", "FL", "32801", "US", + ""); + // Wallet only provides a full name, so the above first and last names + // will be ignored when the profile is written to the DB. + GetServerProfiles.back().SetRawInfo(NAME_FULL, ASCIIToUTF16("John Doe")); + GetServerProfiles.back().set_use_count(100); + + // Add a similar server profile. + GetServerProfiles.push_back( + AutofillProfile(AutofillProfile::SERVER_PROFILE, kServerAddressId2)); + test::SetProfileInfo(&GetServerProfiles.back(), "John", "", "Doe", + "john@doe.com", "Fox", "1212 Center", "Bld. 5", + "Orlando", "FL", "", "US", ""); + // Wallet only provides a full name, so the above first and last names + // will be ignored when the profile is written to the DB. + GetServerProfiles.back().SetRawInfo(NAME_FULL, ASCIIToUTF16("John Doe")); + GetServerProfiles.back().set_use_count(200); + autofill_table_->SetServerProfiles(GetServerProfiles); + + // Add a server and a local card that have the first and second Wallet address + // as a billing address. + CreditCard local_card("287151C8-6AB1-487C-9095-28E80BE5DA15", + "https://www.example.com"); + test::SetCreditCardInfo(&local_card, "Clyde Barrow", + "347666888555" /* American Express */, "04", "2999"); + local_card.set_billing_address_id(kServerAddressId); + personal_data_->AddCreditCard(local_card); + + std::vector<CreditCard> server_cards; + server_cards.push_back( + CreditCard(CreditCard::MASKED_SERVER_CARD, "server_card1")); + test::SetCreditCardInfo(&server_cards.back(), "John Dillinger", + "1111" /* Visa */, "01", "2999"); + server_cards.back().SetTypeForMaskedCard(kVisaCard); + server_cards.back().set_billing_address_id(kServerAddressId2); + test::SetServerCreditCards(autofill_table_, server_cards); + + // Make sure everything is setup correctly. + personal_data_->Refresh(); + EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()) + .WillOnce(QuitMainMessageLoop()); + base::RunLoop().Run(); + EXPECT_EQ(1U, personal_data_->web_profiles().size()); + EXPECT_EQ(2U, personal_data_->GetServerProfiles().size()); + EXPECT_EQ(2U, personal_data_->GetCreditCards().size()); + + /////////////////////////////////////////////////////////////////////// + // Tested method. + /////////////////////////////////////////////////////////////////////// + personal_data_->ConvertWalletAddressesAndUpdateWalletCards(); + + /////////////////////////////////////////////////////////////////////// + // Validation. + /////////////////////////////////////////////////////////////////////// + EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()) + .WillOnce(QuitMainMessageLoop()); + base::RunLoop().Run(); + + // The first Wallet address should have been added as a new local profile and + // the second one should have merged with the first. + EXPECT_EQ(2U, personal_data_->web_profiles().size()); + EXPECT_EQ(2U, personal_data_->GetServerProfiles().size()); + histogram_tester.ExpectBucketCount("Autofill.WalletAddressConversionType", + AutofillMetrics::CONVERTED_ADDRESS_ADDED, + 1); + histogram_tester.ExpectBucketCount("Autofill.WalletAddressConversionType", + AutofillMetrics::CONVERTED_ADDRESS_MERGED, + 1); + + // The conversion should be recorded in the Wallet addresses. + EXPECT_TRUE(personal_data_->GetServerProfiles()[0]->has_converted()); + EXPECT_TRUE(personal_data_->GetServerProfiles()[1]->has_converted()); + + // Get the profiles, sorted by frecency to have a deterministic order. + std::vector<AutofillProfile*> profiles = + personal_data_->GetProfilesToSuggest(); + + // Make sure that the two Wallet addresses merged together and were added as + // a new local profile. + ASSERT_EQ(2U, profiles.size()); + EXPECT_EQ(UTF8ToUTF16("John"), profiles[0]->GetRawInfo(NAME_FIRST)); + EXPECT_EQ(local_profile, *profiles[1]); + + // Check that the values were merged. + EXPECT_EQ(UTF8ToUTF16("Fox"), profiles[0]->GetRawInfo(COMPANY_NAME)); + EXPECT_EQ(UTF8ToUTF16("32801"), profiles[0]->GetRawInfo(ADDRESS_HOME_ZIP)); + + // Make sure that the billing address id of the two cards now point to the + // converted profile. + EXPECT_EQ(profiles[0]->guid(), + personal_data_->GetCreditCards()[0]->billing_address_id()); + EXPECT_EQ(profiles[0]->guid(), + personal_data_->GetCreditCards()[1]->billing_address_id()); +} + +// Tests a new server card's billing address is updated propely even if the +// address was already converted in the past. +TEST_F( + PersonalDataManagerTest, + ConvertWalletAddressesAndUpdateWalletCards_NewCard_AddressAlreadyConverted) { + /////////////////////////////////////////////////////////////////////// + // Setup. + /////////////////////////////////////////////////////////////////////// + // Go through the conversion process for a server address and card. Then add + // a new server card that refers to the already converted server address as + // its billing address. + EnableWalletCardImport(); + base::HistogramTester histogram_tester; + const std::string kServerAddressId("server_address1"); + + // Add a server profile. + std::vector<AutofillProfile> GetServerProfiles; + GetServerProfiles.push_back( + AutofillProfile(AutofillProfile::SERVER_PROFILE, kServerAddressId)); + test::SetProfileInfo(&GetServerProfiles.back(), "John", "", "Doe", "", "Fox", + "1212 Center", "Bld. 5", "Orlando", "FL", "", "US", ""); + // Wallet only provides a full name, so the above first and last names + // will be ignored when the profile is written to the DB. + GetServerProfiles.back().SetRawInfo(NAME_FULL, ASCIIToUTF16("John Doe")); + GetServerProfiles.back().set_use_count(100); + autofill_table_->SetServerProfiles(GetServerProfiles); + + // Add a server card that have the server address as billing address. + std::vector<CreditCard> server_cards; + server_cards.push_back( + CreditCard(CreditCard::MASKED_SERVER_CARD, "server_card1")); + test::SetCreditCardInfo(&server_cards.back(), "John Dillinger", + "1111" /* Visa */, "01", "2999"); + server_cards.back().SetTypeForMaskedCard(kVisaCard); + server_cards.back().set_billing_address_id(kServerAddressId); + test::SetServerCreditCards(autofill_table_, server_cards); + + // Make sure everything is setup correctly. + personal_data_->Refresh(); + EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()) + .WillOnce(QuitMainMessageLoop()); + base::RunLoop().Run(); + EXPECT_EQ(1U, personal_data_->GetServerProfiles().size()); + EXPECT_EQ(1U, personal_data_->GetCreditCards().size()); + + // Run the conversion. + personal_data_->ConvertWalletAddressesAndUpdateWalletCards(); + + EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()) + .WillOnce(QuitMainMessageLoop()); + base::RunLoop().Run(); + + // The Wallet address should have been converted to a new local profile. + EXPECT_EQ(1U, personal_data_->web_profiles().size()); + + // The conversion should be recorded in the Wallet address. + EXPECT_TRUE(personal_data_->GetServerProfiles().back()->has_converted()); + + // Make sure that the billing address id of the card now point to the + // converted profile. + std::vector<AutofillProfile*> profiles = + personal_data_->GetProfilesToSuggest(); + ASSERT_EQ(1U, profiles.size()); + EXPECT_EQ(profiles[0]->guid(), + personal_data_->GetCreditCards()[0]->billing_address_id()); + + // Add a new server card that has the same billing address as the old one. + server_cards.push_back( + CreditCard(CreditCard::MASKED_SERVER_CARD, "server_card2")); + test::SetCreditCardInfo(&server_cards.back(), "John Dillinger", + "1112" /* Visa */, "01", "2888"); + server_cards.back().SetTypeForMaskedCard(kVisaCard); + server_cards.back().set_billing_address_id(kServerAddressId); + test::SetServerCreditCards(autofill_table_, server_cards); + + // Make sure everything is setup correctly. + personal_data_->Refresh(); + EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()) + .WillOnce(QuitMainMessageLoop()); + base::RunLoop().Run(); + EXPECT_EQ(1U, personal_data_->web_profiles().size()); + EXPECT_EQ(2U, personal_data_->GetCreditCards().size()); + + /////////////////////////////////////////////////////////////////////// + // Tested method. + /////////////////////////////////////////////////////////////////////// + personal_data_->ConvertWalletAddressesAndUpdateWalletCards(); + + /////////////////////////////////////////////////////////////////////// + // Validation. + /////////////////////////////////////////////////////////////////////// + EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()) + .WillOnce(QuitMainMessageLoop()); + base::RunLoop().Run(); + + // The conversion should still be recorded in the Wallet address. + EXPECT_TRUE(personal_data_->GetServerProfiles().back()->has_converted()); + + // Get the profiles, sorted by frecency to have a deterministic order. + profiles = personal_data_->GetProfilesToSuggest(); + + // Make sure that there is still only one profile. + ASSERT_EQ(1U, profiles.size()); + + // Make sure that the billing address id of the first server card still refers + // to the converted address. + EXPECT_EQ(profiles[0]->guid(), + personal_data_->GetCreditCards()[0]->billing_address_id()); + // Make sure that the billing address id of the new server card still refers + // to the converted address. + EXPECT_EQ(profiles[0]->guid(), + personal_data_->GetCreditCards()[1]->billing_address_id()); +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/proto/server.proto b/chromium/components/autofill/core/browser/proto/server.proto index 75451977735..e84e2025a79 100644 --- a/chromium/components/autofill/core/browser/proto/server.proto +++ b/chromium/components/autofill/core/browser/proto/server.proto @@ -33,7 +33,7 @@ message AutofillQueryResponseContents { // This message contains information about the field types in a single form. // It is sent by the toolbar to contribute to the field type statistics. -// Next available id: 21 +// Next available id: 22 message AutofillUploadContents { required string client_version = 1; required fixed64 form_signature = 2; @@ -59,7 +59,8 @@ message AutofillUploadContents { // AutoFillFieldType required fixed32 autofill_type = 7; - // The value of the name attribute on the field, if present. + // The value of the name attribute on the field, if present. Otherwise, the + // value of the id attribute. See HTMLFormControlElement::nameForAutofill. optional string name = 8; // The value of the autocomplete attribute on the field, if present. @@ -93,6 +94,10 @@ message AutofillUploadContents { // The properties mask (i.e. whether the field was autofilled, user // modified, etc.) See FieldPropertiesFlags. optional uint32 properties_mask = 20; + + // The value of the id attribute, if it differs from the name attribute. + // Otherwise, this field is absent. + optional string id = 21; } // Signature of the form action host (e.g. Hash64Bit("example.com")). optional fixed64 action_signature = 13; diff --git a/chromium/components/autofill/core/browser/test_autofill_client.cc b/chromium/components/autofill/core/browser/test_autofill_client.cc index 7df6c300ce4..72b2da47cc1 100644 --- a/chromium/components/autofill/core/browser/test_autofill_client.cc +++ b/chromium/components/autofill/core/browser/test_autofill_client.cc @@ -41,6 +41,10 @@ rappor::RapporServiceImpl* TestAutofillClient::GetRapporServiceImpl() { return rappor_service_.get(); } +ukm::UkmService* TestAutofillClient::GetUkmService() { + return ukm_service_test_harness_.test_ukm_service(); +} + void TestAutofillClient::ShowAutofillSettings() { } diff --git a/chromium/components/autofill/core/browser/test_autofill_client.h b/chromium/components/autofill/core/browser/test_autofill_client.h index 72cdd8c3f57..5b25ecfabab 100644 --- a/chromium/components/autofill/core/browser/test_autofill_client.h +++ b/chromium/components/autofill/core/browser/test_autofill_client.h @@ -16,6 +16,7 @@ #include "components/autofill/core/browser/autofill_client.h" #include "components/prefs/pref_service.h" #include "components/rappor/test_rappor_service.h" +#include "components/ukm/test_ukm_service.h" #include "google_apis/gaia/fake_identity_provider.h" #include "google_apis/gaia/fake_oauth2_token_service.h" @@ -34,6 +35,7 @@ class TestAutofillClient : public AutofillClient { syncer::SyncService* GetSyncService() override; IdentityProvider* GetIdentityProvider() override; rappor::RapporServiceImpl* GetRapporServiceImpl() override; + ukm::UkmService* GetUkmService() override; void ShowAutofillSettings() override; void ShowUnmaskPrompt(const CreditCard& card, UnmaskCardReason reason, @@ -85,12 +87,17 @@ class TestAutofillClient : public AutofillClient { void set_form_origin(const GURL& url) { form_origin_ = url; } + ukm::TestUkmService* GetTestUkmService() { + return ukm_service_test_harness_.test_ukm_service(); + } + 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::TestRapporServiceImpl> rappor_service_; + ukm::UkmServiceTestingHarness ukm_service_test_harness_; GURL form_origin_; DISALLOW_COPY_AND_ASSIGN(TestAutofillClient); diff --git a/chromium/components/autofill/core/browser/test_autofill_clock.cc b/chromium/components/autofill/core/browser/test_autofill_clock.cc new file mode 100644 index 00000000000..4d5829b8a26 --- /dev/null +++ b/chromium/components/autofill/core/browser/test_autofill_clock.cc @@ -0,0 +1,33 @@ +// 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/test_autofill_clock.h" + +#include <utility> + +#include "base/test/simple_test_clock.h" +#include "components/autofill/core/common/autofill_clock.h" + +namespace autofill { + +TestAutofillClock::TestAutofillClock() { + // Create a new test clock and set it as the AutofillClock clock and keep a + // pointer to manipulate the time it returns. + std::unique_ptr<base::SimpleTestClock> unique_test_clock( + new base::SimpleTestClock()); + // Keep a pointer to the clock to be able to use its SetNow() function. + test_clock_ = unique_test_clock.get(); + AutofillClock::SetTestClock(std::move(unique_test_clock)); +} + +TestAutofillClock::~TestAutofillClock() { + // Destroys the test clock and resets a normal clock. + AutofillClock::SetClock(); +} + +void TestAutofillClock::SetNow(base::Time now) { + test_clock_->SetNow(now); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/test_autofill_clock.h b/chromium/components/autofill/core/browser/test_autofill_clock.h new file mode 100644 index 00000000000..a40f54fbd2f --- /dev/null +++ b/chromium/components/autofill/core/browser/test_autofill_clock.h @@ -0,0 +1,39 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_TEST_AUTOFILL_CLOCK_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_TEST_AUTOFILL_CLOCK_H_ + +#include <memory> + +#include "base/macros.h" + +namespace base { +class SimpleTestClock; +class Time; +} // namespace base + +namespace autofill { + +// Handles the customization of the time in tests. Replaces the clock in +// AutofillClock with a test version that can be manipulated from this class. +// Automatically resets a normal clock to AutofillClock when this gets +// destroyed, +class TestAutofillClock { + public: + TestAutofillClock(); + ~TestAutofillClock(); + + // Set the time to be returned from AutofillClock::Now() calls. + void SetNow(base::Time now); + + private: + base::SimpleTestClock* test_clock_; + + DISALLOW_COPY_AND_ASSIGN(TestAutofillClock); +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_TEST_AUTOFILL_CLOCK_H_ diff --git a/chromium/components/autofill/core/browser/test_personal_data_manager.cc b/chromium/components/autofill/core/browser/test_personal_data_manager.cc index 1463fdbec48..d663cdefe55 100644 --- a/chromium/components/autofill/core/browser/test_personal_data_manager.cc +++ b/chromium/components/autofill/core/browser/test_personal_data_manager.cc @@ -35,7 +35,7 @@ void TestPersonalDataManager::AddTestingServerCreditCard( const std::vector<AutofillProfile*>& TestPersonalDataManager::GetProfiles() const { - return profiles_; + return GetProfiles(false); } std::vector<AutofillProfile*> TestPersonalDataManager::web_profiles() const { @@ -72,4 +72,9 @@ const std::string& TestPersonalDataManager::GetDefaultCountryCodeForNewAddress() return default_country_code_; } +const std::vector<AutofillProfile*>& TestPersonalDataManager::GetProfiles( + bool record_metrics) const { + return profiles_; +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/test_personal_data_manager.h b/chromium/components/autofill/core/browser/test_personal_data_manager.h index f3192922267..71e06180cbe 100644 --- a/chromium/components/autofill/core/browser/test_personal_data_manager.h +++ b/chromium/components/autofill/core/browser/test_personal_data_manager.h @@ -56,6 +56,9 @@ class TestPersonalDataManager : public PersonalDataManager { const CreditCard& imported_credit_card() { return imported_credit_card_; } private: + const std::vector<AutofillProfile*>& GetProfiles( + bool record_metrics) const override; + std::vector<AutofillProfile*> profiles_; std::vector<CreditCard*> credit_cards_; AutofillProfile imported_profile_; 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 435e00caf82..38778caf3bd 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 @@ -16,10 +16,11 @@ #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_clock.h" #include "components/autofill/core/common/autofill_pref_names.h" +#include "components/grit/components_scaled_resources.h" #include "components/prefs/pref_service.h" -#include "grit/components_scaled_resources.h" -#include "grit/components_strings.h" +#include "components/strings/grit/components_strings.h" #include "ui/base/l10n/l10n_util.h" namespace autofill { @@ -50,7 +51,7 @@ void CardUnmaskPromptControllerImpl::ShowPrompt( card_unmask_view_->ControllerGone(); new_card_link_clicked_ = false; - shown_timestamp_ = base::Time::Now(); + shown_timestamp_ = AutofillClock::Now(); pending_response_ = CardUnmaskDelegate::UnmaskResponse(); card_unmask_view_ = card_unmask_view; card_ = card; @@ -107,8 +108,8 @@ void CardUnmaskPromptControllerImpl::OnVerificationResult( unmasking_result_ = result; AutofillMetrics::LogRealPanResult(result); - AutofillMetrics::LogUnmaskingDuration(base::Time::Now() - verify_timestamp_, - result); + AutofillMetrics::LogUnmaskingDuration( + AutofillClock::Now() - verify_timestamp_, result); card_unmask_view_->GotVerificationResult(error_message, AllowsRetry(result)); } @@ -124,14 +125,14 @@ void CardUnmaskPromptControllerImpl::LogOnCloseEvents() { AutofillMetrics::UnmaskPromptEvent close_reason_event = GetCloseReasonEvent(); AutofillMetrics::LogUnmaskPromptEvent(close_reason_event); AutofillMetrics::LogUnmaskPromptEventDuration( - base::Time::Now() - shown_timestamp_, close_reason_event); + AutofillClock::Now() - shown_timestamp_, close_reason_event); if (close_reason_event == AutofillMetrics::UNMASK_PROMPT_CLOSED_NO_ATTEMPTS) return; if (close_reason_event == AutofillMetrics::UNMASK_PROMPT_CLOSED_ABANDON_UNMASKING) { - AutofillMetrics::LogTimeBeforeAbandonUnmasking(base::Time::Now() - + AutofillMetrics::LogTimeBeforeAbandonUnmasking(AutofillClock::Now() - verify_timestamp_); } @@ -186,7 +187,7 @@ void CardUnmaskPromptControllerImpl::OnUnmaskResponse( const base::string16& exp_month, const base::string16& exp_year, bool should_store_pan) { - verify_timestamp_ = base::Time::Now(); + verify_timestamp_ = AutofillClock::Now(); unmasking_number_of_attempts_++; unmasking_result_ = AutofillClient::NONE; card_unmask_view_->DisableAndWaitForVerification(); @@ -256,7 +257,7 @@ int CardUnmaskPromptControllerImpl::GetCvcImageRid() const { } bool CardUnmaskPromptControllerImpl::ShouldRequestExpirationDate() const { - return card_.ShouldUpdateExpiration(base::Time::Now()) || + return card_.ShouldUpdateExpiration(AutofillClock::Now()) || new_card_link_clicked_; } @@ -300,12 +301,12 @@ bool CardUnmaskPromptControllerImpl::InputExpirationIsValid( // Convert 2 digit year to 4 digit year. if (year_value < 100) { base::Time::Exploded now; - base::Time::Now().LocalExplode(&now); + AutofillClock::Now().LocalExplode(&now); year_value += (now.year / 100) * 100; } return IsValidCreditCardExpirationDate(year_value, month_value, - base::Time::Now()); + AutofillClock::Now()); } base::TimeDelta CardUnmaskPromptControllerImpl::GetSuccessMessageDuration() diff --git a/chromium/components/autofill/core/browser/validation.cc b/chromium/components/autofill/core/browser/validation.cc index 15a96e6ab22..d18ccbab5cf 100644 --- a/chromium/components/autofill/core/browser/validation.cc +++ b/chromium/components/autofill/core/browser/validation.cc @@ -6,14 +6,19 @@ #include <stddef.h> +#include "base/logging.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_piece.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" +#include "components/autofill/core/browser/autofill_data_util.h" #include "components/autofill/core/browser/credit_card.h" #include "components/autofill/core/browser/state_names.h" +#include "components/autofill/core/common/autofill_clock.h" #include "components/autofill/core/common/autofill_regexes.h" +#include "components/strings/grit/components_strings.h" +#include "ui/base/l10n/l10n_util.h" namespace autofill { @@ -94,6 +99,31 @@ bool IsValidCreditCardSecurityCode(const base::string16& code, base::ContainsOnlyChars(code, base::ASCIIToUTF16("0123456789")); } +bool IsValidCreditCardNumberForBasicCardNetworks( + const base::string16& text, + const std::set<std::string>& supported_basic_card_networks, + base::string16* error_message) { + DCHECK(error_message); + + // The type check is cheaper than the credit card number check. + const std::string basic_card_payment_type = + autofill::data_util::GetPaymentRequestData( + CreditCard::GetCreditCardType(text)) + .basic_card_payment_type; + if (!supported_basic_card_networks.count(basic_card_payment_type)) { + *error_message = l10n_util::GetStringUTF16( + IDS_PAYMENTS_VALIDATION_UNSUPPORTED_CREDIT_CARD_TYPE); + return false; + } + + if (IsValidCreditCardNumber(text)) + return true; + + *error_message = l10n_util::GetStringUTF16( + IDS_PAYMENTS_CARD_NUMBER_INVALID_VALIDATION_MESSAGE); + return false; +} + bool IsValidEmailAddress(const base::string16& text) { // E-Mail pattern as defined by the WhatWG. (4.10.7.1.5 E-Mail state) const base::string16 kEmailPattern = base::ASCIIToUTF16( @@ -174,4 +204,108 @@ bool IsSSN(const base::string16& text) { return true; } +bool IsValidForType(const base::string16& value, + ServerFieldType type, + base::string16* error_message) { + switch (type) { + case CREDIT_CARD_NAME_FULL: + if (!value.empty()) + return true; + + if (error_message) { + *error_message = + l10n_util::GetStringUTF16(IDS_PAYMENTS_VALIDATION_INVALID_NAME); + } + break; + + case CREDIT_CARD_EXP_MONTH: { + CreditCard temp; + // Expiration month was in an invalid format. + temp.SetExpirationMonthFromString(value, /* app_locale= */ std::string()); + if (temp.expiration_month() == 0) { + if (error_message) { + *error_message = l10n_util::GetStringUTF16( + IDS_PAYMENTS_VALIDATION_INVALID_CREDIT_CARD_EXPIRATION_MONTH); + } + break; + } + return true; + } + + case CREDIT_CARD_EXP_2_DIGIT_YEAR: + case CREDIT_CARD_EXP_4_DIGIT_YEAR: { + CreditCard temp; + temp.SetExpirationYearFromString(value); + // Expiration year was in an invalid format. + if ((temp.expiration_year() == 0) || + (type == CREDIT_CARD_EXP_2_DIGIT_YEAR && value.size() != 2u) || + (type == CREDIT_CARD_EXP_4_DIGIT_YEAR && value.size() != 4u)) { + if (error_message) { + *error_message = l10n_util::GetStringUTF16( + IDS_PAYMENTS_VALIDATION_INVALID_CREDIT_CARD_EXPIRATION_YEAR); + } + break; + } + + base::Time::Exploded now_exploded; + AutofillClock::Now().LocalExplode(&now_exploded); + if (temp.expiration_year() >= now_exploded.year) + return true; + + // If the year is before this year, it's expired. + if (error_message) { + *error_message = l10n_util::GetStringUTF16( + IDS_PAYMENTS_VALIDATION_INVALID_CREDIT_CARD_EXPIRED); + } + break; + } + + case CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR: + case CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR: { + const base::string16 pattern = + type == CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR + ? base::UTF8ToUTF16("^[0-9]{1,2}[-/|]?[0-9]{2}$") + : base::UTF8ToUTF16("^[0-9]{1,2}[-/|]?[0-9]{4}$"); + + CreditCard temp; + temp.SetExpirationDateFromString(value); + + // Expiration date was in an invalid format. + if (temp.expiration_month() == 0 || temp.expiration_year() == 0 || + !MatchesPattern(value, pattern)) { + if (error_message) { + *error_message = l10n_util::GetStringUTF16( + IDS_PAYMENTS_CARD_EXPIRATION_INVALID_VALIDATION_MESSAGE); + } + break; + } + + // Checking for card expiration. + if (IsValidCreditCardExpirationDate(temp.expiration_year(), + temp.expiration_month(), + AutofillClock::Now())) { + return true; + } + + if (error_message) { + *error_message = l10n_util::GetStringUTF16( + IDS_PAYMENTS_VALIDATION_INVALID_CREDIT_CARD_EXPIRED); + } + break; + } + + case CREDIT_CARD_NUMBER: + NOTREACHED() << "IsValidCreditCardNumberForBasicCardNetworks should be " + << "used to validate credit card numbers"; + break; + + default: + // Other types such as CREDIT_CARD_TYPE and CREDIT_CARD_VERIFICATION_CODE + // are not validated for now. + NOTREACHED() << "Attempting to validate unsupported type " << type; + break; + } + return false; +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/validation.h b/chromium/components/autofill/core/browser/validation.h index a09ae75cbb3..0b496e2b950 100644 --- a/chromium/components/autofill/core/browser/validation.h +++ b/chromium/components/autofill/core/browser/validation.h @@ -7,6 +7,7 @@ #include "base/strings/string16.h" #include "base/strings/string_piece.h" +#include "components/autofill/core/browser/field_types.h" namespace base { class Time; @@ -29,11 +30,19 @@ bool IsValidCreditCardNumber(const base::string16& text); bool IsValidCreditCardSecurityCode(const base::string16& code, const base::StringPiece card_type); +// Returns true if |text| is a supported card type and a valid credit card +// number. |error_message| can't be null and will be filled with the appropriate +// error message. +bool IsValidCreditCardNumberForBasicCardNetworks( + const base::string16& text, + const std::set<std::string>& supported_basic_card_networks, + base::string16* error_message); + // Returns true if |text| looks like a valid e-mail address. bool IsValidEmailAddress(const base::string16& text); -// Returns true if |text| is a valid US state name or abbreviation. It is -// case insensitive. Valid for US states only. +// Returns true if |text| is a valid US state name or abbreviation. It is case +// insensitive. Valid for US states only. bool IsValidState(const base::string16& text); // Returns true if |text| looks like a valid zip code. @@ -43,6 +52,12 @@ bool IsValidZip(const base::string16& text); // Returns true if |text| looks like an SSN, with or without separators. bool IsSSN(const base::string16& text); +// Returns whether |value| is valid for the given |type|. If not null, +// |error_message| is populated when the function returns false. +bool IsValidForType(const base::string16& value, + ServerFieldType type, + base::string16* error_message); + } // namespace autofill #endif // COMPONENTS_AUTOFILL_CORE_BROWSER_VALIDATION_H_ diff --git a/chromium/components/autofill/core/browser/validation_unittest.cc b/chromium/components/autofill/core/browser/validation_unittest.cc index 798f53462bd..ec6ff0a346f 100644 --- a/chromium/components/autofill/core/browser/validation_unittest.cc +++ b/chromium/components/autofill/core/browser/validation_unittest.cc @@ -2,14 +2,19 @@ // 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/validation.h" + #include <stddef.h> #include "base/macros.h" +#include "base/strings/string16.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 "components/autofill/core/browser/field_types.h" +#include "components/strings/grit/components_strings.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/base/l10n/l10n_util.h" using base::ASCIIToUTF16; @@ -33,24 +38,24 @@ struct SecurityCodeCardTypePair { // From https://www.paypalobjects.com/en_US/vhelp/paypalmanager_help/credit_card_numbers.htm const char* const kValidNumbers[] = { - "378282246310005", - "3714 4963 5398 431", - "3787-3449-3671-000", - "5610591081018250", - "3056 9309 0259 04", - "3852-0000-0232-37", - "6011111111111117", - "6011 0009 9013 9424", - "3530-1113-3330-0000", - "3566002020360505", - "5555 5555 5555 4444", - "5105-1051-0510-5100", - "4111111111111111", - "4012 8888 8888 1881", - "4222-2222-2222-2", - "5019717010103742", - "6331101999990016", - "6247130048162403", + "378282246310005", + "3714 4963 5398 431", + "3787-3449-3671-000", + "5610591081018250", + "3056 9309 0259 04", + "3852-0000-0232-37", + "6011111111111117", + "6011 0009 9013 9424", + "3530-1113-3330-0000", + "3566002020360505", + "5555 5555 5555 4444", // Mastercard. + "5105-1051-0510-5100", + "4111111111111111", // Visa. + "4012 8888 8888 1881", + "4222-2222-2222-2", + "5019717010103742", + "6331101999990016", + "6247130048162403", }; const char* const kInvalidNumbers[] = { "4111 1111 112", /* too short */ @@ -148,4 +153,263 @@ TEST(AutofillValidation, IsValidEmailAddress) { } } +struct ValidationCase { + ValidationCase(const char* value, + ServerFieldType field_type, + bool expected_valid, + int expected_error_id) + : value(value), + field_type(field_type), + expected_valid(expected_valid), + expected_error_id(expected_error_id) {} + ~ValidationCase() {} + + const char* const value; + const ServerFieldType field_type; + const bool expected_valid; + const int expected_error_id; +}; + +class AutofillTypeValidationTest + : public testing::TestWithParam<ValidationCase> {}; + +TEST_P(AutofillTypeValidationTest, IsValidForType) { + base::string16 error_message; + EXPECT_EQ(GetParam().expected_valid, + IsValidForType(ASCIIToUTF16(GetParam().value), + GetParam().field_type, &error_message)) + << "Failed to validate " << GetParam().value << " (type " + << GetParam().field_type << ")"; + if (!GetParam().expected_valid) { + EXPECT_EQ(l10n_util::GetStringUTF16(GetParam().expected_error_id), + error_message); + } +} + +INSTANTIATE_TEST_CASE_P( + CreditCardExpDate, + AutofillTypeValidationTest, + testing::Values( + ValidationCase("05/2087", CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, true, 0), + ValidationCase("05-2087", CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, true, 0), + ValidationCase("052087", CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, true, 0), + ValidationCase("05|2087", CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, true, 0), + + ValidationCase("05/2012", + CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, + false, + IDS_PAYMENTS_VALIDATION_INVALID_CREDIT_CARD_EXPIRED), + ValidationCase("MM/2012", + CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, + false, + IDS_PAYMENTS_CARD_EXPIRATION_INVALID_VALIDATION_MESSAGE), + ValidationCase("05/12", + CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, + false, + IDS_PAYMENTS_CARD_EXPIRATION_INVALID_VALIDATION_MESSAGE), + ValidationCase("05/45", + CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, + false, + IDS_PAYMENTS_CARD_EXPIRATION_INVALID_VALIDATION_MESSAGE), + ValidationCase("05/1987", + CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, + false, + IDS_PAYMENTS_CARD_EXPIRATION_INVALID_VALIDATION_MESSAGE), + + ValidationCase("05/87", CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, true, 0), + ValidationCase("05-87", CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, true, 0), + ValidationCase("0587", CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, true, 0), + ValidationCase("05|87", CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, true, 0), + ValidationCase("05/1987", + CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, + false, + IDS_PAYMENTS_CARD_EXPIRATION_INVALID_VALIDATION_MESSAGE), + ValidationCase("05/12", + CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, + false, + IDS_PAYMENTS_VALIDATION_INVALID_CREDIT_CARD_EXPIRED))); + +INSTANTIATE_TEST_CASE_P( + CreditCardMonth, + AutofillTypeValidationTest, + testing::Values( + ValidationCase("01", CREDIT_CARD_EXP_MONTH, true, 0), + ValidationCase("1", CREDIT_CARD_EXP_MONTH, true, 0), + ValidationCase("12", CREDIT_CARD_EXP_MONTH, true, 0), + ValidationCase( + "0", + CREDIT_CARD_EXP_MONTH, + false, + IDS_PAYMENTS_VALIDATION_INVALID_CREDIT_CARD_EXPIRATION_MONTH), + ValidationCase( + "-1", + CREDIT_CARD_EXP_MONTH, + false, + IDS_PAYMENTS_VALIDATION_INVALID_CREDIT_CARD_EXPIRATION_MONTH), + ValidationCase( + "13", + CREDIT_CARD_EXP_MONTH, + false, + IDS_PAYMENTS_VALIDATION_INVALID_CREDIT_CARD_EXPIRATION_MONTH))); + +INSTANTIATE_TEST_CASE_P( + CreditCardYear, + AutofillTypeValidationTest, + testing::Values( + /* 2-digit year */ + ValidationCase("87", CREDIT_CARD_EXP_2_DIGIT_YEAR, true, 0), + // These are considered expired in the context of this millenium. + ValidationCase("02", + CREDIT_CARD_EXP_2_DIGIT_YEAR, + false, + IDS_PAYMENTS_VALIDATION_INVALID_CREDIT_CARD_EXPIRED), + ValidationCase("15", + CREDIT_CARD_EXP_2_DIGIT_YEAR, + false, + IDS_PAYMENTS_VALIDATION_INVALID_CREDIT_CARD_EXPIRED), + // Invalid formats. + ValidationCase( + "1", + CREDIT_CARD_EXP_2_DIGIT_YEAR, + false, + IDS_PAYMENTS_VALIDATION_INVALID_CREDIT_CARD_EXPIRATION_YEAR), + ValidationCase( + "123", + CREDIT_CARD_EXP_2_DIGIT_YEAR, + false, + IDS_PAYMENTS_VALIDATION_INVALID_CREDIT_CARD_EXPIRATION_YEAR), + ValidationCase( + "2087", + CREDIT_CARD_EXP_2_DIGIT_YEAR, + false, + IDS_PAYMENTS_VALIDATION_INVALID_CREDIT_CARD_EXPIRATION_YEAR), + + /* 4-digit year */ + ValidationCase("2087", CREDIT_CARD_EXP_4_DIGIT_YEAR, true, 0), + // Expired. + ValidationCase("2000", + CREDIT_CARD_EXP_4_DIGIT_YEAR, + false, + IDS_PAYMENTS_VALIDATION_INVALID_CREDIT_CARD_EXPIRED), + ValidationCase("2015", + CREDIT_CARD_EXP_4_DIGIT_YEAR, + false, + IDS_PAYMENTS_VALIDATION_INVALID_CREDIT_CARD_EXPIRED), + // Invalid formats. + ValidationCase( + "00", + CREDIT_CARD_EXP_4_DIGIT_YEAR, + false, + IDS_PAYMENTS_VALIDATION_INVALID_CREDIT_CARD_EXPIRATION_YEAR), + ValidationCase( + "123", + CREDIT_CARD_EXP_4_DIGIT_YEAR, + false, + IDS_PAYMENTS_VALIDATION_INVALID_CREDIT_CARD_EXPIRATION_YEAR), + ValidationCase( + "87", + CREDIT_CARD_EXP_4_DIGIT_YEAR, + false, + IDS_PAYMENTS_VALIDATION_INVALID_CREDIT_CARD_EXPIRATION_YEAR))); + +struct CCNumberCase { + CCNumberCase(const char* value, + const std::set<std::string> supported_basic_card_networks, + bool expected_valid, + int expected_error_id) + : value(value), + supported_basic_card_networks(supported_basic_card_networks), + expected_valid(expected_valid), + expected_error_id(expected_error_id) {} + ~CCNumberCase() {} + + const char* const value; + const std::set<std::string> supported_basic_card_networks; + const bool expected_valid; + const int expected_error_id; +}; + +class AutofillCCNumberValidationTest + : public testing::TestWithParam<CCNumberCase> {}; + +TEST_P(AutofillCCNumberValidationTest, IsValidCreditCardNumber) { + base::string16 error_message; + EXPECT_EQ(GetParam().expected_valid, + IsValidCreditCardNumberForBasicCardNetworks( + ASCIIToUTF16(GetParam().value), + GetParam().supported_basic_card_networks, &error_message)) + << "Failed to validate CC number " << GetParam().value; + if (!GetParam().expected_valid) { + EXPECT_EQ(l10n_util::GetStringUTF16(GetParam().expected_error_id), + error_message); + } +} + +const static std::set<std::string> kAllBasicCardNetworks{ + "amex", "discover", "diners", "jcb", + "mastercard", "mir", "unionpay", "visa"}; + +INSTANTIATE_TEST_CASE_P( + CreditCardNumber, + AutofillCCNumberValidationTest, + testing::Values( + CCNumberCase(kValidNumbers[0], kAllBasicCardNetworks, true, 0), + CCNumberCase(kValidNumbers[1], kAllBasicCardNetworks, true, 0), + CCNumberCase(kValidNumbers[2], kAllBasicCardNetworks, true, 0), + // Generic card not supported. + CCNumberCase(kValidNumbers[3], + kAllBasicCardNetworks, + false, + IDS_PAYMENTS_VALIDATION_UNSUPPORTED_CREDIT_CARD_TYPE), + + CCNumberCase(kValidNumbers[4], kAllBasicCardNetworks, true, 0), + CCNumberCase(kValidNumbers[5], kAllBasicCardNetworks, true, 0), + CCNumberCase(kValidNumbers[6], kAllBasicCardNetworks, true, 0), + CCNumberCase(kValidNumbers[7], kAllBasicCardNetworks, true, 0), + CCNumberCase(kValidNumbers[8], kAllBasicCardNetworks, true, 0), + CCNumberCase(kValidNumbers[9], kAllBasicCardNetworks, true, 0), + CCNumberCase(kValidNumbers[10], kAllBasicCardNetworks, true, 0), + CCNumberCase(kValidNumbers[11], kAllBasicCardNetworks, true, 0), + CCNumberCase(kValidNumbers[12], kAllBasicCardNetworks, true, 0), + CCNumberCase(kValidNumbers[13], kAllBasicCardNetworks, true, 0), + CCNumberCase(kValidNumbers[14], kAllBasicCardNetworks, true, 0), + // Generic cards not supported. + CCNumberCase(kValidNumbers[15], + kAllBasicCardNetworks, + false, + IDS_PAYMENTS_VALIDATION_UNSUPPORTED_CREDIT_CARD_TYPE), + CCNumberCase(kValidNumbers[16], + kAllBasicCardNetworks, + false, + IDS_PAYMENTS_VALIDATION_UNSUPPORTED_CREDIT_CARD_TYPE), + + CCNumberCase(kValidNumbers[17], kAllBasicCardNetworks, true, 0), + + CCNumberCase(kInvalidNumbers[0], + kAllBasicCardNetworks, + false, + IDS_PAYMENTS_CARD_NUMBER_INVALID_VALIDATION_MESSAGE), + CCNumberCase(kInvalidNumbers[1], + kAllBasicCardNetworks, + false, + IDS_PAYMENTS_CARD_NUMBER_INVALID_VALIDATION_MESSAGE), + CCNumberCase(kInvalidNumbers[2], + kAllBasicCardNetworks, + false, + IDS_PAYMENTS_CARD_NUMBER_INVALID_VALIDATION_MESSAGE), + CCNumberCase(kInvalidNumbers[3], + kAllBasicCardNetworks, + false, + IDS_PAYMENTS_CARD_NUMBER_INVALID_VALIDATION_MESSAGE), + + // Valid numbers can still be invalid if the type is not supported. + CCNumberCase(kValidNumbers[10], // Mastercard number. + {"visa"}, + false, + IDS_PAYMENTS_VALIDATION_UNSUPPORTED_CREDIT_CARD_TYPE), + CCNumberCase(kValidNumbers[12], // Visa number. + {"jcb", "diners", "unionpay", "mastercard"}, + false, + IDS_PAYMENTS_VALIDATION_UNSUPPORTED_CREDIT_CARD_TYPE))); + } // 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 index 0bf47d314ba..589df1628af 100644 --- a/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc +++ b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc @@ -35,6 +35,7 @@ using syncer::EntityDataMap; using syncer::MetadataChangeList; using syncer::ModelError; using syncer::ModelTypeChangeProcessor; +using syncer::ModelTypeSyncBridge; using syncer::MutableDataBatch; namespace autofill { @@ -57,15 +58,21 @@ void* UserDataKey() { return reinterpret_cast<void*>(&user_data_key); } +std::string EscapeIdentifiers(const AutofillSpecifics& specifics) { + return net::EscapePath(specifics.name()) + + std::string(kAutocompleteTagDelimiter) + + net::EscapePath(specifics.value()); +} + 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()); + entity_data->non_unique_name = EscapeIdentifiers(*autofill); return entity_data; } @@ -143,17 +150,19 @@ class SyncDifferenceTracker { 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); - } + } else { unique_to_local_.erase(local.value()); + 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); + } + } } return {}; } @@ -283,10 +292,11 @@ void AutocompleteSyncBridge::CreateForWebDataServiceAndBackend( } // static -AutocompleteSyncBridge* AutocompleteSyncBridge::FromWebDataService( +base::WeakPtr<ModelTypeSyncBridge> AutocompleteSyncBridge::FromWebDataService( AutofillWebDataService* web_data_service) { return static_cast<AutocompleteSyncBridge*>( - web_data_service->GetDBUserData()->GetUserData(UserDataKey())); + web_data_service->GetDBUserData()->GetUserData(UserDataKey())) + ->AsWeakPtr(); } AutocompleteSyncBridge::AutocompleteSyncBridge( @@ -318,8 +328,7 @@ Optional<syncer::ModelError> AutocompleteSyncBridge::MergeSyncData( EntityDataMap entity_data_map) { DCHECK(thread_checker_.CalledOnValidThread()); - // TODO(skym, crbug.com/680218): Uncomment and add unit tests. - /*SyncDifferenceTracker tracker(GetAutofillTable()); + SyncDifferenceTracker tracker(GetAutofillTable()); for (auto kv : entity_data_map) { DCHECK(kv.second->specifics.has_autofill()); RETURN_IF_ERROR(tracker.IncorporateRemoteSpecifics( @@ -330,7 +339,7 @@ Optional<syncer::ModelError> AutocompleteSyncBridge::MergeSyncData( RETURN_IF_ERROR(tracker.FlushToSync(true, std::move(metadata_change_list), change_processor())); web_data_backend_->RemoveExpiredFormElements(); - web_data_backend_->NotifyThatSyncHasStarted(syncer::AUTOFILL);*/ + web_data_backend_->NotifyThatSyncHasStarted(syncer::AUTOFILL); return {}; } @@ -449,11 +458,8 @@ void AutocompleteSyncBridge::LoadMetadata() { 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()); + EscapeIdentifiers(entity_data.specifics.autofill()); } std::string AutocompleteSyncBridge::GetStorageKey( diff --git a/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h index cd350bec322..2ec41c5ae51 100644 --- a/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h +++ b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h @@ -9,6 +9,7 @@ #include <string> #include "base/macros.h" +#include "base/memory/weak_ptr.h" #include "base/optional.h" #include "base/scoped_observer.h" #include "base/supports_user_data.h" @@ -39,7 +40,7 @@ class AutocompleteSyncBridge : public base::SupportsUserData::Data, AutofillWebDataService* web_data_service, AutofillWebDataBackend* web_data_backend); - static AutocompleteSyncBridge* FromWebDataService( + static base::WeakPtr<syncer::ModelTypeSyncBridge> FromWebDataService( AutofillWebDataService* web_data_service); // syncer::ModelTypeSyncBridge implementation. diff --git a/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc index b12d0ef223f..a4334eaa230 100644 --- a/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc +++ b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc @@ -6,6 +6,7 @@ #include <algorithm> #include <map> +#include <utility> #include <vector> #include "base/bind.h" @@ -33,12 +34,15 @@ using base::ScopedTempDir; using base::Time; using base::TimeDelta; using sync_pb::AutofillSpecifics; +using sync_pb::EntityMetadata; using sync_pb::EntitySpecifics; +using sync_pb::ModelTypeState; using syncer::DataBatch; -using syncer::EntityData; -using syncer::EntityDataPtr; using syncer::EntityChange; using syncer::EntityChangeList; +using syncer::EntityData; +using syncer::EntityDataPtr; +using syncer::EntityDataMap; using syncer::FakeModelTypeChangeProcessor; using syncer::KeyAndData; using syncer::ModelError; @@ -130,20 +134,18 @@ class AutocompleteSyncBridgeTest : public testing::Test { 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)))); + ResetBridge(); } } ~AutocompleteSyncBridgeTest() override {} + void ResetBridge() { + bridge_.reset(new AutocompleteSyncBridge( + &backend_, + base::Bind(&AutocompleteSyncBridgeTest::CreateModelTypeChangeProcessor, + base::Unretained(this)))); + } + void SaveSpecificsToTable( const std::vector<AutofillSpecifics>& specifics_list) { std::vector<AutofillEntry> new_entries; @@ -177,6 +179,13 @@ class AutocompleteSyncBridgeTest : public testing::Test { return CreateSpecifics(suffix, std::vector<int>{0}); } + std::string GetClientTag(const AutofillSpecifics& specifics) { + std::string tag = + bridge()->GetClientTag(SpecificsToEntity(specifics).value()); + EXPECT_FALSE(tag.empty()); + return tag; + } + std::string GetStorageKey(const AutofillSpecifics& specifics) { std::string key = bridge()->GetStorageKey(SpecificsToEntity(specifics).value()); @@ -194,6 +203,15 @@ class AutocompleteSyncBridgeTest : public testing::Test { return changes; } + EntityDataMap CreateEntityDataMap( + const std::vector<AutofillSpecifics>& specifics_vector) { + EntityDataMap map; + for (const auto& specifics : specifics_vector) { + map[GetStorageKey(specifics)] = SpecificsToEntity(specifics); + } + return map; + } + void VerifyApplyChanges(const std::vector<EntityChange>& changes) { const auto error = bridge()->ApplySyncChanges( bridge()->CreateMetadataChangeList(), changes); @@ -204,6 +222,12 @@ class AutocompleteSyncBridgeTest : public testing::Test { VerifyApplyChanges(EntityAddList(specifics)); } + void VerifyMerge(const std::vector<AutofillSpecifics>& specifics) { + const auto error = bridge()->MergeSyncData( + bridge()->CreateMetadataChangeList(), CreateEntityDataMap(specifics)); + EXPECT_FALSE(error); + } + std::map<std::string, AutofillSpecifics> ExpectedMap( const std::vector<AutofillSpecifics>& specifics_vector) { std::map<std::string, AutofillSpecifics> map; @@ -265,7 +289,50 @@ class AutocompleteSyncBridgeTest : public testing::Test { }; TEST_F(AutocompleteSyncBridgeTest, GetClientTag) { - // TODO(skym, crbug.com/675991): Implementation. + std::string tag = GetClientTag(CreateSpecifics(1)); + EXPECT_EQ(tag, GetClientTag(CreateSpecifics(1))); + EXPECT_NE(tag, GetClientTag(CreateSpecifics(2))); +} + +TEST_F(AutocompleteSyncBridgeTest, GetClientTagNotAffectedByTimestamp) { + AutofillSpecifics specifics = CreateSpecifics(1); + std::string tag = GetClientTag(specifics); + + specifics.add_usage_timestamp(1); + EXPECT_EQ(tag, GetClientTag(specifics)); + + specifics.add_usage_timestamp(0); + EXPECT_EQ(tag, GetClientTag(specifics)); + + specifics.add_usage_timestamp(-1); + EXPECT_EQ(tag, GetClientTag(specifics)); +} + +TEST_F(AutocompleteSyncBridgeTest, GetClientTagRespectsNullCharacter) { + AutofillSpecifics specifics; + std::string tag = GetClientTag(specifics); + + specifics.set_value(std::string("\0", 1)); + EXPECT_NE(tag, GetClientTag(specifics)); +} + +// The client tags should never change as long as we want to maintain backwards +// compatibility with the previous iteration of autocomplete-sync integration, +// AutocompleteSyncableService and Sync's Directory. This is because old clients +// will re-generate client tags and then hashes on local changes, and this +// process must create identical values to what this client has created. If this +// test case starts failing, you should not alter the fixed values here unless +// you know what you're doing. +TEST_F(AutocompleteSyncBridgeTest, GetClientTagFixed) { + EXPECT_EQ("autofill_entry|name%201|value%201", + GetClientTag(CreateSpecifics(1))); + EXPECT_EQ("autofill_entry|name%202|value%202", + GetClientTag(CreateSpecifics(2))); + EXPECT_EQ("autofill_entry||", GetClientTag(AutofillSpecifics())); + AutofillSpecifics specifics; + specifics.set_name("\xEC\xA4\x91"); + specifics.set_value("\xD0\x80"); + EXPECT_EQ("autofill_entry|%EC%A4%91|%D0%80", GetClientTag(specifics)); } TEST_F(AutocompleteSyncBridgeTest, GetStorageKey) { @@ -274,8 +341,7 @@ TEST_F(AutocompleteSyncBridgeTest, GetStorageKey) { EXPECT_NE(key, GetStorageKey(CreateSpecifics(2))); } -// Timestamps should not affect storage keys. -TEST_F(AutocompleteSyncBridgeTest, GetStorageKeyTimestamp) { +TEST_F(AutocompleteSyncBridgeTest, GetStorageKeyNotAffectedByTimestamp) { AutofillSpecifics specifics = CreateSpecifics(1); std::string key = GetStorageKey(specifics); @@ -289,8 +355,7 @@ TEST_F(AutocompleteSyncBridgeTest, GetStorageKeyTimestamp) { EXPECT_EQ(key, GetStorageKey(specifics)); } -// Verify that the \0 character is respected as a difference. -TEST_F(AutocompleteSyncBridgeTest, GetStorageKeyNull) { +TEST_F(AutocompleteSyncBridgeTest, GetStorageKeyRespectsNullCharacter) { AutofillSpecifics specifics; std::string key = GetStorageKey(specifics); @@ -517,8 +582,108 @@ TEST_F(AutocompleteSyncBridgeTest, LocalEntryDeleted) { } TEST_F(AutocompleteSyncBridgeTest, LoadMetadataCalled) { - EXPECT_NE(processor()->metadata(), nullptr); + EXPECT_NE(nullptr, processor()->metadata()); + EXPECT_FALSE( + processor()->metadata()->GetModelTypeState().initial_sync_done()); + EXPECT_EQ(0u, processor()->metadata()->TakeAllMetadata().size()); + + ModelTypeState model_type_state; + model_type_state.set_initial_sync_done(true); + EXPECT_TRUE( + table()->UpdateModelTypeState(syncer::AUTOFILL, model_type_state)); + EXPECT_TRUE( + table()->UpdateSyncMetadata(syncer::AUTOFILL, "key", EntityMetadata())); + + ResetBridge(); + + EXPECT_NE(nullptr, processor()->metadata()); EXPECT_TRUE(processor()->metadata()->GetModelTypeState().initial_sync_done()); + EXPECT_EQ(1u, processor()->metadata()->TakeAllMetadata().size()); +} + +TEST_F(AutocompleteSyncBridgeTest, MergeSyncDataEmpty) { + VerifyMerge(std::vector<AutofillSpecifics>()); + + VerifyAllData(std::vector<AutofillSpecifics>()); + EXPECT_EQ(0u, processor()->delete_set().size()); + EXPECT_EQ(0u, processor()->put_multimap().size()); +} + +TEST_F(AutocompleteSyncBridgeTest, MergeSyncDataRemoteOnly) { + const AutofillSpecifics specifics1 = CreateSpecifics(1, {2}); + const AutofillSpecifics specifics2 = CreateSpecifics(2, {3, 4}); + + VerifyMerge({specifics1, specifics2}); + + VerifyAllData({specifics1, specifics2}); + EXPECT_EQ(0u, processor()->delete_set().size()); + EXPECT_EQ(0u, processor()->put_multimap().size()); +} + +TEST_F(AutocompleteSyncBridgeTest, MergeSyncDataLocalOnly) { + const AutofillSpecifics specifics1 = CreateSpecifics(1, {2}); + const AutofillSpecifics specifics2 = CreateSpecifics(2, {3, 4}); + VerifyApplyAdds({specifics1, specifics2}); + VerifyAllData({specifics1, specifics2}); + + VerifyMerge(std::vector<AutofillSpecifics>()); + + VerifyAllData({specifics1, specifics2}); + EXPECT_EQ(2u, processor()->put_multimap().size()); + VerifyProcessorRecordedPut(specifics1); + VerifyProcessorRecordedPut(specifics2); + EXPECT_EQ(0u, processor()->delete_set().size()); +} + +TEST_F(AutocompleteSyncBridgeTest, MergeSyncDataAllMerged) { + const AutofillSpecifics local1 = CreateSpecifics(1, {2}); + const AutofillSpecifics local2 = CreateSpecifics(2, {3, 4}); + const AutofillSpecifics local3 = CreateSpecifics(3, {4}); + const AutofillSpecifics local4 = CreateSpecifics(4, {5, 6}); + const AutofillSpecifics local5 = CreateSpecifics(5, {6, 9}); + const AutofillSpecifics local6 = CreateSpecifics(6, {7, 9}); + const AutofillSpecifics remote1 = local1; + const AutofillSpecifics remote2 = local2; + const AutofillSpecifics remote3 = CreateSpecifics(3, {5}); + const AutofillSpecifics remote4 = CreateSpecifics(4, {7, 8}); + const AutofillSpecifics remote5 = CreateSpecifics(5, {8, 9}); + const AutofillSpecifics remote6 = CreateSpecifics(6, {8, 10}); + const AutofillSpecifics merged1 = local1; + const AutofillSpecifics merged2 = local2; + const AutofillSpecifics merged3 = CreateSpecifics(3, {4, 5}); + const AutofillSpecifics merged4 = CreateSpecifics(4, {5, 8}); + const AutofillSpecifics merged5 = local5; + const AutofillSpecifics merged6 = CreateSpecifics(6, {7, 10}); + VerifyApplyAdds({local1, local2, local3, local4, local5, local6}); + + VerifyMerge({remote1, remote2, remote3, remote4, remote5, remote6}); + + VerifyAllData({merged1, merged2, merged3, merged4, merged5, merged6}); + EXPECT_EQ(4u, processor()->put_multimap().size()); + VerifyProcessorRecordedPut(merged3); + VerifyProcessorRecordedPut(merged4); + VerifyProcessorRecordedPut(merged5); + VerifyProcessorRecordedPut(merged6); + EXPECT_EQ(0u, processor()->delete_set().size()); +} + +TEST_F(AutocompleteSyncBridgeTest, MergeSyncDataMixed) { + const AutofillSpecifics local1 = CreateSpecifics(1, {2, 3}); + const AutofillSpecifics remote2 = CreateSpecifics(2, {2, 3}); + const AutofillSpecifics specifics3 = CreateSpecifics(3, {2, 3}); + const AutofillSpecifics local4 = CreateSpecifics(4, {1, 3}); + const AutofillSpecifics remote4 = CreateSpecifics(4, {2, 4}); + const AutofillSpecifics merged4 = CreateSpecifics(4, {1, 4}); + + VerifyApplyAdds({local1, specifics3, local4}); + + VerifyMerge({remote2, specifics3, remote4}); + + VerifyAllData({local1, remote2, specifics3, merged4}); + EXPECT_EQ(2u, processor()->put_multimap().size()); + VerifyProcessorRecordedPut(local1); + VerifyProcessorRecordedPut(merged4); + EXPECT_EQ(0u, processor()->delete_set().size()); } } // 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 aff312184ea..d303d165ae5 100644 --- a/chromium/components/autofill/core/browser/webdata/autocomplete_syncable_service.cc +++ b/chromium/components/autofill/core/browser/webdata/autocomplete_syncable_service.cc @@ -309,8 +309,7 @@ void AutocompleteSyncableService::CreateOrUpdateEntry( return; } - AutofillKey key(autofill_specifics.name().c_str(), - autofill_specifics.value().c_str()); + AutofillKey key(autofill_specifics.name(), autofill_specifics.value()); AutocompleteEntryMap::iterator it = loaded_data->find(key); const google::protobuf::RepeatedField<int64_t>& timestamps = autofill_specifics.usage_timestamp(); diff --git a/chromium/components/autofill/core/browser/webdata/autofill_entry.cc b/chromium/components/autofill/core/browser/webdata/autofill_entry.cc index 58494a7c93e..3a48a50c10e 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_entry.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_entry.cc @@ -4,6 +4,7 @@ #include "components/autofill/core/browser/webdata/autofill_entry.h" +#include <string> #include <tuple> #include "base/strings/utf_string_conversions.h" @@ -18,7 +19,8 @@ AutofillKey::AutofillKey(const base::string16& name, value_(value) { } -AutofillKey::AutofillKey(const char* name, const char* value) +AutofillKey::AutofillKey(const std::string& name, + const std::string& value) : name_(base::UTF8ToUTF16(name)), value_(base::UTF8ToUTF16(value)) { } diff --git a/chromium/components/autofill/core/browser/webdata/autofill_entry.h b/chromium/components/autofill/core/browser/webdata/autofill_entry.h index 4a934dd4510..a7932fde2cd 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_entry.h +++ b/chromium/components/autofill/core/browser/webdata/autofill_entry.h @@ -5,6 +5,8 @@ #ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_ENTRY_H__ #define COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_ENTRY_H__ +#include <string> + #include "base/strings/string16.h" #include "base/time/time.h" @@ -14,7 +16,7 @@ class AutofillKey { public: AutofillKey(); AutofillKey(const base::string16& name, const base::string16& value); - AutofillKey(const char* name, const char* value); + AutofillKey(const std::string& name, const std::string& value); AutofillKey(const AutofillKey& key); virtual ~AutofillKey(); diff --git a/chromium/components/autofill/core/browser/webdata/autofill_table.cc b/chromium/components/autofill/core/browser/webdata/autofill_table.cc index 503eb0fa9e7..72cf35b3c7b 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_table.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_table.cc @@ -11,8 +11,7 @@ #include <limits> #include <map> #include <set> -#include <string> -#include <vector> +#include <utility> #include "base/command_line.h" #include "base/guid.h" @@ -31,12 +30,13 @@ #include "components/autofill/core/browser/personal_data_manager.h" #include "components/autofill/core/browser/webdata/autofill_change.h" #include "components/autofill/core/browser/webdata/autofill_entry.h" +#include "components/autofill/core/browser/webdata/autofill_table_encryptor.h" +#include "components/autofill/core/browser/webdata/autofill_table_encryptor_factory.h" +#include "components/autofill/core/common/autofill_clock.h" #include "components/autofill/core/common/autofill_switches.h" #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" @@ -130,17 +130,19 @@ std::unique_ptr<AutofillProfile> AutofillProfileFromStatement( } void BindEncryptedCardToColumn(sql::Statement* s, - int column_index, - const base::string16& number) { + int column_index, + const base::string16& number, + const AutofillTableEncryptor& encryptor) { std::string encrypted_data; - OSCrypt::EncryptString16(number, &encrypted_data); + encryptor.EncryptString16(number, &encrypted_data); s->BindBlob(column_index, encrypted_data.data(), static_cast<int>(encrypted_data.length())); } void BindCreditCardToStatement(const CreditCard& credit_card, const Time& modification_date, - sql::Statement* s) { + sql::Statement* s, + const AutofillTableEncryptor& encryptor) { DCHECK(base::IsValidGUID(credit_card.guid())); int index = 0; s->BindString(index++, credit_card.guid()); @@ -148,8 +150,8 @@ void BindCreditCardToStatement(const CreditCard& credit_card, s->BindString16(index++, GetInfo(credit_card, CREDIT_CARD_NAME_FULL)); s->BindString16(index++, GetInfo(credit_card, CREDIT_CARD_EXP_MONTH)); s->BindString16(index++, GetInfo(credit_card, CREDIT_CARD_EXP_4_DIGIT_YEAR)); - BindEncryptedCardToColumn(s, index++, - credit_card.GetRawInfo(CREDIT_CARD_NUMBER)); + BindEncryptedCardToColumn( + s, index++, credit_card.GetRawInfo(CREDIT_CARD_NUMBER), encryptor); s->BindInt64(index++, credit_card.use_count()); s->BindInt64(index++, credit_card.use_date().ToTimeT()); @@ -158,8 +160,10 @@ void BindCreditCardToStatement(const CreditCard& credit_card, s->BindString(index++, credit_card.billing_address_id()); } -base::string16 UnencryptedCardFromColumn(const sql::Statement& s, - int column_index) { +base::string16 UnencryptedCardFromColumn( + const sql::Statement& s, + int column_index, + const AutofillTableEncryptor& encryptor) { base::string16 credit_card_number; int encrypted_number_len = s.ColumnByteLength(column_index); if (encrypted_number_len) { @@ -167,12 +171,14 @@ base::string16 UnencryptedCardFromColumn(const sql::Statement& s, encrypted_number.resize(encrypted_number_len); memcpy(&encrypted_number[0], s.ColumnBlob(column_index), encrypted_number_len); - OSCrypt::DecryptString16(encrypted_number, &credit_card_number); + encryptor.DecryptString16(encrypted_number, &credit_card_number); } return credit_card_number; } -std::unique_ptr<CreditCard> CreditCardFromStatement(const sql::Statement& s) { +std::unique_ptr<CreditCard> CreditCardFromStatement( + const sql::Statement& s, + const AutofillTableEncryptor& encryptor) { std::unique_ptr<CreditCard> credit_card(new CreditCard); int index = 0; @@ -184,7 +190,7 @@ std::unique_ptr<CreditCard> CreditCardFromStatement(const sql::Statement& s) { credit_card->SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, s.ColumnString16(index++)); credit_card->SetRawInfo(CREDIT_CARD_NUMBER, - UnencryptedCardFromColumn(s, index++)); + UnencryptedCardFromColumn(s, index++, encryptor)); credit_card->set_use_count(s.ColumnInt64(index++)); credit_card->set_use_date(Time::FromTimeT(s.ColumnInt64(index++))); credit_card->set_modification_date(Time::FromTimeT(s.ColumnInt64(index++))); @@ -400,7 +406,10 @@ base::string16 Substitute(const base::string16& s, // static const size_t AutofillTable::kMaxDataLength = 1024; -AutofillTable::AutofillTable() { +AutofillTable::AutofillTable() + : autofill_table_encryptor_( + AutofillTableEncryptorFactory::GetInstance()->Create()) { + DCHECK(autofill_table_encryptor_); } AutofillTable::~AutofillTable() { @@ -481,12 +490,12 @@ bool AutofillTable::MigrateToVersion(int version, bool AutofillTable::AddFormFieldValues( const std::vector<FormFieldData>& elements, std::vector<AutofillChange>* changes) { - return AddFormFieldValuesTime(elements, changes, Time::Now()); + return AddFormFieldValuesTime(elements, changes, AutofillClock::Now()); } bool AutofillTable::AddFormFieldValue(const FormFieldData& element, std::vector<AutofillChange>* changes) { - return AddFormFieldValueTime(element, changes, Time::Now()); + return AddFormFieldValueTime(element, changes, AutofillClock::Now()); } bool AutofillTable::GetFormValuesForElementName( @@ -674,7 +683,7 @@ bool AutofillTable::RemoveFormElementsAddedBetween( bool AutofillTable::RemoveExpiredFormElements( std::vector<AutofillChange>* changes) { Time expiration_time = - Time::Now() - TimeDelta::FromDays(kExpirationPeriodInDays); + AutofillClock::Now() - TimeDelta::FromDays(kExpirationPeriodInDays); // Query for the name and value of all form elements that were last used // before the |expiration_time|. @@ -886,7 +895,7 @@ bool AutofillTable::AddAutofillProfile(const AutofillProfile& profile) { " zipcode, sorting_code, country_code, use_count, use_date, " " date_modified, origin, language_code)" "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)")); - BindAutofillProfileToStatement(profile, Time::Now(), &s); + BindAutofillProfileToStatement(profile, AutofillClock::Now(), &s); if (!s.Run()) return false; @@ -950,21 +959,22 @@ bool AutofillTable::GetServerProfiles( sql::Statement s(db_->GetUniqueStatement( "SELECT " - "id," - "use_count," - "use_date," - "recipient_name," - "company_name," - "street_address," - "address_1," // ADDRESS_HOME_STATE - "address_2," // ADDRESS_HOME_CITY - "address_3," // ADDRESS_HOME_DEPENDENT_LOCALITY - "address_4," // Not supported in AutofillProfile yet. - "postal_code," // ADDRESS_HOME_ZIP - "sorting_code," // ADDRESS_HOME_SORTING_CODE - "country_code," // ADDRESS_HOME_COUNTRY - "phone_number," // PHONE_HOME_WHOLE_NUMBER - "language_code " + "id," + "use_count," + "use_date," + "recipient_name," + "company_name," + "street_address," + "address_1," // ADDRESS_HOME_STATE + "address_2," // ADDRESS_HOME_CITY + "address_3," // ADDRESS_HOME_DEPENDENT_LOCALITY + "address_4," // Not supported in AutofillProfile yet. + "postal_code," // ADDRESS_HOME_ZIP + "sorting_code," // ADDRESS_HOME_SORTING_CODE + "country_code," // ADDRESS_HOME_COUNTRY + "phone_number," // PHONE_HOME_WHOLE_NUMBER + "language_code, " + "has_converted " "FROM server_addresses addresses " "LEFT OUTER JOIN server_address_metadata USING (id)")); @@ -976,7 +986,7 @@ bool AutofillTable::GetServerProfiles( profile->set_use_count(s.ColumnInt64(index++)); profile->set_use_date(Time::FromInternalValue(s.ColumnInt64(index++))); // Modification date is not tracked for server profiles. Explicitly set it - // here to override the default value of Time::Now(). + // here to override the default value of AutofillClock::Now(). profile->set_modification_date(Time()); base::string16 recipient_name = s.ColumnString16(index++); @@ -992,6 +1002,7 @@ bool AutofillTable::GetServerProfiles( profile->SetRawInfo(ADDRESS_HOME_COUNTRY, s.ColumnString16(index++)); base::string16 phone_number = s.ColumnString16(index++); profile->set_language_code(s.ColumnString(index++)); + profile->set_has_converted(s.ColumnBool(index++)); // SetInfo instead of SetRawInfo so the constituent pieces will be parsed // for these data types. @@ -1055,6 +1066,9 @@ void AutofillTable::SetServerProfiles( insert.Run(); insert.Reset(true); + + // Save the use count and use date of the profile. + UpdateServerAddressMetadata(profile); } // Delete metadata that's no longer relevant. @@ -1087,10 +1101,11 @@ bool AutofillTable::UpdateAutofillProfile(const AutofillProfile& profile) { " city=?, state=?, zipcode=?, sorting_code=?, country_code=?, " " use_count=?, use_date=?, date_modified=?, origin=?, language_code=? " "WHERE guid=?")); - BindAutofillProfileToStatement( - profile, - update_modification_date ? Time::Now() : old_profile->modification_date(), - &s); + BindAutofillProfileToStatement(profile, + update_modification_date + ? AutofillClock::Now() + : old_profile->modification_date(), + &s); s.BindString(14, profile.guid()); bool result = s.Run(); @@ -1160,7 +1175,8 @@ bool AutofillTable::AddCreditCard(const CreditCard& credit_card) { " card_number_encrypted, use_count, use_date, date_modified, origin," " billing_address_id)" "VALUES (?,?,?,?,?,?,?,?,?,?)")); - BindCreditCardToStatement(credit_card, Time::Now(), &s); + BindCreditCardToStatement(credit_card, AutofillClock::Now(), &s, + *autofill_table_encryptor_); if (!s.Run()) return false; @@ -1183,7 +1199,7 @@ std::unique_ptr<CreditCard> AutofillTable::GetCreditCard( if (!s.Step()) return std::unique_ptr<CreditCard>(); - return CreditCardFromStatement(s); + return CreditCardFromStatement(s, *autofill_table_encryptor_); } bool AutofillTable::GetCreditCards( @@ -1232,7 +1248,8 @@ bool AutofillTable::GetServerCreditCards( // If the card_number_encrypted field is nonempty, we can assume this card // is a full card, otherwise it's masked. - base::string16 full_card_number = UnencryptedCardFromColumn(s, index++); + base::string16 full_card_number = + UnencryptedCardFromColumn(s, index++, *autofill_table_encryptor_); base::string16 last_four = s.ColumnString16(index++); CreditCard::RecordType record_type = full_card_number.empty() ? CreditCard::MASKED_SERVER_CARD : @@ -1248,7 +1265,7 @@ bool AutofillTable::GetServerCreditCards( card->set_use_count(s.ColumnInt64(index++)); card->set_use_date(Time::FromInternalValue(s.ColumnInt64(index++))); // Modification date is not tracked for server cards. Explicitly set it here - // to override the default value of Time::Now(). + // to override the default value of AutofillClock::Now(). card->set_modification_date(Time()); std::string card_type = s.ColumnString(index++); @@ -1339,10 +1356,10 @@ bool AutofillTable::UnmaskServerCreditCard(const CreditCard& masked, s.BindString(0, masked.server_id()); std::string encrypted_data; - OSCrypt::EncryptString16(full_number, &encrypted_data); + autofill_table_encryptor_->EncryptString16(full_number, &encrypted_data); s.BindBlob(1, encrypted_data.data(), static_cast<int>(encrypted_data.length())); - s.BindInt64(2, Time::Now().ToInternalValue()); // unmask_date + s.BindInt64(2, AutofillClock::Now().ToInternalValue()); // unmask_date s.Run(); @@ -1410,7 +1427,7 @@ bool AutofillTable::UpdateServerAddressMetadata( "VALUES (?,?,?,?)")); s.BindInt64(0, profile.use_count()); s.BindInt64(1, profile.use_date().ToInternalValue()); - s.BindBool(2, false); + s.BindBool(2, profile.has_converted()); s.BindString(3, profile.server_id()); s.Run(); @@ -1469,11 +1486,11 @@ bool AutofillTable::UpdateCreditCard(const CreditCard& credit_card) { "expiration_year=?, card_number_encrypted=?, use_count=?, use_date=?," "date_modified=?, origin=?, billing_address_id=?" "WHERE guid=?1")); - BindCreditCardToStatement( - credit_card, - update_modification_date ? Time::Now() : - old_credit_card->modification_date(), - &s); + BindCreditCardToStatement(credit_card, + update_modification_date + ? AutofillClock::Now() + : old_credit_card->modification_date(), + &s, *autofill_table_encryptor_); bool result = s.Run(); DCHECK_GT(db_->GetLastChangeCount(), 0); @@ -1686,7 +1703,7 @@ bool AutofillTable::GetAllSyncMetadata(syncer::ModelType model_type, syncer::EntityMetadataMap metadata_records; if (GetAllSyncEntityMetadata(model_type, &metadata_records)) { for (const auto& pair : metadata_records) { - // todo(pnoland): add batch transfer of metadata map + // TODO(pnoland): Add batch transfer of metadata map. metadata_batch->AddMetadata(pair.first, pair.second); } } else { @@ -1762,7 +1779,7 @@ bool AutofillTable::GetModelTypeState(syncer::ModelType model_type, "SELECT value FROM autofill_model_type_state WHERE id=1")); if (!s.Step()) { - return false; + return true; } std::string serialized_state = s.ColumnString(0); @@ -2527,4 +2544,4 @@ bool AutofillTable:: return transaction.Commit(); } -} // namespace autofill
\ No newline at end of file +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/webdata/autofill_table.h b/chromium/components/autofill/core/browser/webdata/autofill_table.h index 2f5fff15431..f3f8a14dcc4 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_table.h +++ b/chromium/components/autofill/core/browser/webdata/autofill_table.h @@ -8,6 +8,7 @@ #include <stddef.h> #include <memory> +#include <string> #include <vector> #include "base/gtest_prod_util.h" @@ -23,16 +24,12 @@ namespace base { class Time; } -namespace sync_pb { -class EntityMetadata; -class ModelTypeState; -} - namespace autofill { class AutofillChange; class AutofillEntry; class AutofillProfile; +class AutofillTableEncryptor; class AutofillTableTest; class CreditCard; @@ -550,6 +547,8 @@ class AutofillTable : public WebDatabaseTable { bool InitAutofillSyncMetadataTable(); bool InitModelTypeStateTable(); + std::unique_ptr<AutofillTableEncryptor> autofill_table_encryptor_; + DISALLOW_COPY_AND_ASSIGN(AutofillTable); }; diff --git a/chromium/components/autofill/core/browser/webdata/autofill_table_encryptor.h b/chromium/components/autofill/core/browser/webdata/autofill_table_encryptor.h new file mode 100644 index 00000000000..2391cd722a2 --- /dev/null +++ b/chromium/components/autofill/core/browser/webdata/autofill_table_encryptor.h @@ -0,0 +1,26 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_TABLE_ENCRYPTOR_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_TABLE_ENCRYPTOR_H_ + +#include <string> + +#include "base/strings/string16.h" + +namespace autofill { +// Encryptor used by Autofill table. +class AutofillTableEncryptor { + public: + virtual ~AutofillTableEncryptor() = default; + + virtual bool EncryptString16(const base::string16& plaintext, + std::string* ciphertext) const = 0; + virtual bool DecryptString16(const std::string& ciphertext, + base::string16* plaintext) const = 0; +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_TABLE_ENCRYPTOR_H_ diff --git a/chromium/components/autofill/core/browser/webdata/autofill_table_encryptor_factory.cc b/chromium/components/autofill/core/browser/webdata/autofill_table_encryptor_factory.cc new file mode 100644 index 00000000000..6ee6516d154 --- /dev/null +++ b/chromium/components/autofill/core/browser/webdata/autofill_table_encryptor_factory.cc @@ -0,0 +1,33 @@ +// 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_table_encryptor_factory.h" + +#include "base/memory/ptr_util.h" +#include "base/memory/singleton.h" +#include "components/autofill/core/browser/webdata/system_encryptor.h" + +namespace autofill { + +AutofillTableEncryptorFactory::AutofillTableEncryptorFactory() = default; + +AutofillTableEncryptorFactory::~AutofillTableEncryptorFactory() = default; + +AutofillTableEncryptorFactory* AutofillTableEncryptorFactory::GetInstance() { + return base::Singleton<AutofillTableEncryptorFactory>::get(); +} + +std::unique_ptr<AutofillTableEncryptor> +AutofillTableEncryptorFactory::Create() { + DCHECK(sequence_checker_.CalledOnValidSequence()); + return delegate_ ? delegate_->Create() : base::MakeUnique<SystemEncryptor>(); +} + +void AutofillTableEncryptorFactory::SetDelegate( + std::unique_ptr<Delegate> delegate) { + DCHECK(sequence_checker_.CalledOnValidSequence()); + delegate_ = std::move(delegate); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/webdata/autofill_table_encryptor_factory.h b/chromium/components/autofill/core/browser/webdata/autofill_table_encryptor_factory.h new file mode 100644 index 00000000000..aa0d52de601 --- /dev/null +++ b/chromium/components/autofill/core/browser/webdata/autofill_table_encryptor_factory.h @@ -0,0 +1,53 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_TABLE_ENCRYPTOR_FACTORY_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_TABLE_ENCRYPTOR_FACTORY_H_ + +#include <memory> + +#include "base/sequence_checker.h" + +namespace base { +template <typename T> +struct DefaultSingletonTraits; +} // namespace base + +namespace autofill { + +class AutofillTableEncryptor; + +// Factory for creating Autofill table encryptor. +// If |delegate_| is set, then |delegate_| is used to create encryptor, +// else default encrytor (SystemEncryptor) is returned. +class AutofillTableEncryptorFactory { + public: + // Embedders are recommended to use this delegate to inject + // their encryptor into Autofill table. + class Delegate { + public: + virtual ~Delegate() = default; + + virtual std::unique_ptr<AutofillTableEncryptor> Create() = 0; + }; + + static AutofillTableEncryptorFactory* GetInstance(); + + std::unique_ptr<AutofillTableEncryptor> Create(); + + void SetDelegate(std::unique_ptr<Delegate> delegate); + + private: + AutofillTableEncryptorFactory(); + ~AutofillTableEncryptorFactory(); + + std::unique_ptr<Delegate> delegate_; + base::SequenceChecker sequence_checker_; + + friend struct base::DefaultSingletonTraits<AutofillTableEncryptorFactory>; + DISALLOW_COPY_AND_ASSIGN(AutofillTableEncryptorFactory); +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_TABLE_ENCRYPTOR_FACTORY_H_ 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 e8da2b12843..235dd06230b 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc @@ -2,14 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include <stddef.h> +#include "components/autofill/core/browser/webdata/autofill_table.h" #include <map> #include <set> -#include <string> #include <tuple> #include <utility> -#include <vector> #include "base/command_line.h" #include "base/files/file_util.h" @@ -29,7 +27,6 @@ #include "components/autofill/core/browser/credit_card.h" #include "components/autofill/core/browser/webdata/autofill_change.h" #include "components/autofill/core/browser/webdata/autofill_entry.h" -#include "components/autofill/core/browser/webdata/autofill_table.h" #include "components/autofill/core/common/autofill_constants.h" #include "components/autofill/core/common/autofill_switches.h" #include "components/autofill/core/common/autofill_util.h" @@ -44,6 +41,10 @@ using base::ASCIIToUTF16; using base::Time; using base::TimeDelta; +using sync_pb::EntityMetadata; +using sync_pb::ModelTypeState; +using syncer::EntityMetadataMap; +using syncer::MetadataBatch; namespace autofill { @@ -85,8 +86,8 @@ bool CompareAutofillEntries(const AutofillEntry& a, const AutofillEntry& b) { b.date_created(), b.date_last_used()); } -AutofillEntry MakeAutofillEntry(const char* name, - const char* value, +AutofillEntry MakeAutofillEntry(const std::string& name, + const std::string& value, time_t date_created, time_t date_last_used) { if (date_last_used < 0) @@ -463,6 +464,31 @@ TEST_F(AutofillTableTest, Autofill_UpdateTwo) { db_.get())); } +TEST_F(AutofillTableTest, Autofill_UpdateNullTerminated) { + const char kName[] = "foo"; + const char kValue[] = "bar"; + // A value which contains terminating character. + std::string value(kValue, arraysize(kValue)); + + AutofillEntry entry0(MakeAutofillEntry(kName, kValue, 1, -1)); + AutofillEntry entry1(MakeAutofillEntry(kName, value, 2, 3)); + std::vector<AutofillEntry> entries; + entries.push_back(entry0); + entries.push_back(entry1); + ASSERT_TRUE(table_->UpdateAutofillEntries(entries)); + + EXPECT_EQ(1, GetAutofillEntryCount(ASCIIToUTF16(kName), ASCIIToUTF16(kValue), + db_.get())); + EXPECT_EQ(2, GetAutofillEntryCount(ASCIIToUTF16(kName), ASCIIToUTF16(value), + db_.get())); + + std::vector<AutofillEntry> all_entries; + ASSERT_TRUE(table_->GetAllAutofillEntries(&all_entries)); + ASSERT_EQ(2U, all_entries.size()); + EXPECT_EQ(entry0, all_entries[0]); + EXPECT_EQ(entry1, all_entries[1]); +} + TEST_F(AutofillTableTest, Autofill_UpdateReplace) { AutofillChangeList changes; // Add a form field. This will be replaced. @@ -1823,8 +1849,8 @@ TEST_F(AutofillTableTest, SetServerProfileUpdateUsageStats) { table_->GetServerProfiles(&outputs); ASSERT_EQ(1u, outputs.size()); EXPECT_EQ(one.server_id(), outputs[0]->server_id()); - EXPECT_EQ(0U, outputs[0]->use_count()); - EXPECT_EQ(base::Time(), outputs[0]->use_date()); + EXPECT_EQ(1U, outputs[0]->use_count()); + EXPECT_NE(base::Time(), outputs[0]->use_date()); // We don't track modification date for server profiles. It should always be // base::Time(). EXPECT_EQ(base::Time(), outputs[0]->modification_date()); @@ -1851,18 +1877,6 @@ TEST_F(AutofillTableTest, SetServerProfileUpdateUsageStats) { EXPECT_NE(base::Time(), outputs[0]->use_date()); EXPECT_EQ(base::Time(), outputs[0]->modification_date()); outputs.clear(); - - // Set a null profile list --- this should clear metadata. - table_->SetServerProfiles(std::vector<AutofillProfile>()); - // Reset the old profile list and see the metadata is reset. - table_->SetServerProfiles(inputs); - table_->GetServerProfiles(&outputs); - ASSERT_EQ(1u, outputs.size()); - EXPECT_EQ(one.server_id(), outputs[0]->server_id()); - EXPECT_EQ(0U, outputs[0]->use_count()); - EXPECT_EQ(base::Time(), outputs[0]->use_date()); - EXPECT_EQ(base::Time(), outputs[0]->modification_date()); - outputs.clear(); } // Tests that deleting time ranges re-masks server credit cards that were @@ -1996,8 +2010,16 @@ TEST_F(AutofillTableTest, GetFormValuesForElementName_SubstringMatchEnabled) { } } -TEST_F(AutofillTableTest, GetAllSyncMetadata) { - sync_pb::EntityMetadata metadata; +TEST_F(AutofillTableTest, AutofillNoMetadata) { + MetadataBatch metadata_batch; + EXPECT_TRUE(table_->GetAllSyncMetadata(syncer::AUTOFILL, &metadata_batch)); + EXPECT_EQ(0u, metadata_batch.TakeAllMetadata().size()); + EXPECT_EQ(ModelTypeState().SerializeAsString(), + metadata_batch.GetModelTypeState().SerializeAsString()); +} + +TEST_F(AutofillTableTest, AutofillGetAllSyncMetadata) { + EntityMetadata metadata; std::string storage_key = "storage_key"; std::string storage_key2 = "storage_key2"; metadata.set_sequence_number(1); @@ -2005,7 +2027,7 @@ TEST_F(AutofillTableTest, GetAllSyncMetadata) { EXPECT_TRUE( table_->UpdateSyncMetadata(syncer::AUTOFILL, storage_key, metadata)); - sync_pb::ModelTypeState model_type_state; + ModelTypeState model_type_state; model_type_state.set_initial_sync_done(true); EXPECT_TRUE(table_->UpdateModelTypeState(syncer::AUTOFILL, model_type_state)); @@ -2014,12 +2036,12 @@ TEST_F(AutofillTableTest, GetAllSyncMetadata) { EXPECT_TRUE( table_->UpdateSyncMetadata(syncer::AUTOFILL, storage_key2, metadata)); - syncer::MetadataBatch metadata_batch; + 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(); + EntityMetadataMap metadata_records = metadata_batch.TakeAllMetadata(); EXPECT_EQ(metadata_records.size(), 2u); EXPECT_EQ(metadata_records[storage_key].sequence_number(), 1); @@ -2033,11 +2055,11 @@ TEST_F(AutofillTableTest, GetAllSyncMetadata) { EXPECT_FALSE(metadata_batch.GetModelTypeState().initial_sync_done()); } -TEST_F(AutofillTableTest, WriteThenDeleteSyncMetadata) { - sync_pb::EntityMetadata metadata; - syncer::MetadataBatch metadata_batch; +TEST_F(AutofillTableTest, AutofillWriteThenDeleteSyncMetadata) { + EntityMetadata metadata; + MetadataBatch metadata_batch; std::string storage_key = "storage_key"; - sync_pb::ModelTypeState model_type_state; + ModelTypeState model_type_state; model_type_state.set_initial_sync_done(true); @@ -2052,32 +2074,35 @@ TEST_F(AutofillTableTest, WriteThenDeleteSyncMetadata) { // It shouldn't be there any more. EXPECT_TRUE(table_->GetAllSyncMetadata(syncer::AUTOFILL, &metadata_batch)); - syncer::EntityMetadataMap metadata_records = metadata_batch.TakeAllMetadata(); + 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)); + EXPECT_TRUE(table_->GetAllSyncMetadata(syncer::AUTOFILL, &metadata_batch)); + EXPECT_EQ(ModelTypeState().SerializeAsString(), + metadata_batch.GetModelTypeState().SerializeAsString()); } -TEST_F(AutofillTableTest, CorruptSyncMetadata) { - syncer::MetadataBatch metadata_batch; - sync_pb::ModelTypeState state; - std::string storage_key = "storage_key"; - +TEST_F(AutofillTableTest, AutofillCorruptSyncMetadata) { + MetadataBatch metadata_batch; sql::Statement s(db_->GetSQLConnection()->GetUniqueStatement( "INSERT OR REPLACE INTO autofill_sync_metadata " "(storage_key, value) VALUES(?, ?)")); - s.BindString(0, storage_key); + s.BindString(0, "storage_key"); s.BindString(1, "unparseable"); + EXPECT_TRUE(s.Run()); - sql::Statement s2(db_->GetSQLConnection()->GetUniqueStatement( + EXPECT_FALSE(table_->GetAllSyncMetadata(syncer::AUTOFILL, &metadata_batch)); +} + +TEST_F(AutofillTableTest, AutofillCorruptModelTypeState) { + MetadataBatch metadata_batch; + sql::Statement s(db_->GetSQLConnection()->GetUniqueStatement( "INSERT OR REPLACE INTO autofill_model_type_state " "(rowid, value) VALUES(1, ?)")); - s2.BindString(0, "unparseable"); - + s.BindString(0, "unparseable"); EXPECT_TRUE(s.Run()); - EXPECT_TRUE(s2.Run()); EXPECT_FALSE(table_->GetAllSyncMetadata(syncer::AUTOFILL, &metadata_batch)); } 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 326ddc4ee15..021b91abd50 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 @@ -33,6 +33,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. @@ -163,20 +167,40 @@ void MergeCommonMetadata( 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) { + + // If the two models have the same metadata, do nothing. + if (local_model->use_count() == remote_use_count && + local_model->use_date() == remote_use_date) { + return; + } + + // Special case for local models with a use_count of one. This means the local + // model was only created, never used. The remote model should always be + // preferred. + // This situation can happen for new Chromium instances where there is no data + // yet on disk, making the use_date artifically high. Once the metadata sync + // kicks in, we should use that value. + if (local_model->use_count() == 1) { local_model->set_use_date(remote_use_date); + local_model->set_use_count(remote_use_count); *is_local_modified = true; - } else if (local_model->use_date() > remote_use_date) { - *is_remote_outdated = true; + } else { + // Otherwise, just keep the most recent use date and biggest use count. + 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; + } + + 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; + } } } @@ -201,6 +225,20 @@ void MergeMetadata(const sync_pb::WalletMetadataSpecifics& remote_metadata, is_local_modified); } +// Whether the |current_billing_address_id| is considered outdated compared to +// the |proposed_billing_address_id|. +bool IsBillingAddressOutdated(const std::string& current_billing_address_id, + const std::string& proposed_billing_address_id) { + DCHECK(current_billing_address_id != proposed_billing_address_id); + + // If the current billing address is empty, or if the current one refers to a + // server address and the proposed one refers to a local address, the current + // billing address is considered outdated. + return current_billing_address_id.empty() || + (current_billing_address_id.size() != kLocalGuidSize && + proposed_billing_address_id.size() == kLocalGuidSize); +} + // Merges the metadata of the remote and local versions of the credit card. void MergeMetadata(const sync_pb::WalletMetadataSpecifics& remote_metadata, CreditCard* local_card, @@ -213,18 +251,21 @@ void MergeMetadata(const sync_pb::WalletMetadataSpecifics& remote_metadata, &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()) { + if (IsBillingAddressOutdated(local_card->billing_address_id(), + remote_billing_address_id)) { local_card->set_billing_address_id(remote_billing_address_id); *is_local_modified = true; - } else if (remote_billing_address_id.empty()) { + } else if (IsBillingAddressOutdated(remote_billing_address_id, + local_card->billing_address_id())) { *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. + // The cards have a different non-empty billing address id and both refer + // to the same type of address. Keep the billing address id of the most + // recently used card. If both have the same timestamp, the remote version + // should be kept in order to stabilize the values. base::Time remote_use_date = base::Time::FromInternalValue(remote_metadata.use_date()); - if (local_card->use_date() < remote_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 { 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 89439d7184c..b24fec6e158 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 @@ -59,6 +59,12 @@ const char kAddr2SyncTag[] = "address-YWRkcjLvv74="; const char kCard1SyncTag[] = "card-Y2FyZDHvv74="; const char kCard2SyncTag[] = "card-Y2FyZDLvv74="; +// Local profile GUID in UTF8 and non-UTF8. +const char kLocalAddr1[] = "e171e3ed-858a-4dd5-9bf3-8517f14ba5fc"; +const char kLocalAddr2[] = "fa232b9a-f248-4e5a-8d76-d46f821c0c5f"; +const char kLocalAddr1Utf8[] = + "ZTE3MWUzZWQtODU4YS00ZGQ1LTliZjMtODUxN2YxNGJhNWZj"; + // Map values are owned by the caller to GetLocalData. ACTION_P2(GetCopiesOf, profiles, cards) { for (const auto& profile : *profiles) { @@ -674,9 +680,9 @@ syncer::SyncChange BuildCardChange( // server. TEST_F(AutofillWalletMetadataSyncableServiceTest, IgnoreNewMetadataFromServerOnSyncChange) { - local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + local_.UpdateAddressStats(BuildAddress(kAddr1, 2, 2, true)); local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); - remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + remote_.UpdateAddressStats(BuildAddress(kAddr1, 2, 2, true)); remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); MergeMetadata(&local_, &remote_); syncer::SyncChangeList changes; @@ -698,9 +704,9 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, // disk when processing on-going sync changes. TEST_F(AutofillWalletMetadataSyncableServiceTest, SaveHigherValuesFromServerOnSyncChange) { - local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, false)); + local_.UpdateAddressStats(BuildAddress(kAddr1, 2, 2, false)); local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); - remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, false)); + remote_.UpdateAddressStats(BuildAddress(kAddr1, 2, 2, false)); remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); MergeMetadata(&local_, &remote_); syncer::SyncChangeList changes; @@ -724,9 +730,9 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, // processing on-going sync changes. TEST_F(AutofillWalletMetadataSyncableServiceTest, SendHigherValuesToServerOnSyncChange) { - local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + local_.UpdateAddressStats(BuildAddress(kAddr1, 2, 2, true)); local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); - remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + remote_.UpdateAddressStats(BuildAddress(kAddr1, 2, 2, true)); remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); MergeMetadata(&local_, &remote_); syncer::SyncChangeList changes; @@ -743,7 +749,7 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, SyncAddressChangeAndDataMatch( syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag, sync_pb::WalletMetadataSpecifics::ADDRESS, - kAddr1Utf8, 1, 2, true), + kAddr1Utf8, 2, 2, true), SyncCardChangeAndDataMatch( syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag, sync_pb::WalletMetadataSpecifics::CARD, @@ -755,9 +761,9 @@ 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, true)); + local_.UpdateAddressStats(BuildAddress(kAddr1, 2, 2, true)); local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); - remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + remote_.UpdateAddressStats(BuildAddress(kAddr1, 2, 2, true)); remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); MergeMetadata(&local_, &remote_); syncer::SyncChangeList changes; @@ -774,7 +780,7 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, SyncAddressChangeAndDataMatch( syncer::SyncChange::ACTION_UPDATE, kAddr1SyncTag, sync_pb::WalletMetadataSpecifics::ADDRESS, - kAddr1Utf8, 1, 2, true), + kAddr1Utf8, 2, 2, true), SyncCardChangeAndDataMatch( syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag, sync_pb::WalletMetadataSpecifics::CARD, @@ -787,9 +793,9 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, // no disk writes and no messages sent to the server. TEST_F(AutofillWalletMetadataSyncableServiceTest, IgnoreUpdateOfUnknownMetadataOnSyncChange) { - local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + local_.UpdateAddressStats(BuildAddress(kAddr1, 2, 2, true)); local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); - remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + remote_.UpdateAddressStats(BuildAddress(kAddr1, 2, 2, true)); remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); MergeMetadata(&local_, &remote_); syncer::SyncChangeList changes; @@ -811,9 +817,9 @@ 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, true)); + local_.UpdateAddressStats(BuildAddress(kAddr1, 2, 2, true)); local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); - remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + remote_.UpdateAddressStats(BuildAddress(kAddr1, 2, 2, true)); remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); MergeMetadata(&local_, &remote_); syncer::SyncChangeList changes; @@ -835,9 +841,9 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, // trigger an undelete message sent to the server. TEST_F(AutofillWalletMetadataSyncableServiceTest, UndeleteExistingMetadataOnSyncChange) { - local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + local_.UpdateAddressStats(BuildAddress(kAddr1, 2, 2, true)); local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); - remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + remote_.UpdateAddressStats(BuildAddress(kAddr1, 2, 2, true)); remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); MergeMetadata(&local_, &remote_); syncer::SyncChangeList changes; @@ -854,7 +860,7 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, SyncAddressChangeAndDataMatch( syncer::SyncChange::ACTION_ADD, kAddr1SyncTag, sync_pb::WalletMetadataSpecifics::ADDRESS, - kAddr1Utf8, 1, 2, true), + kAddr1Utf8, 2, 2, true), SyncCardChangeAndDataMatch( syncer::SyncChange::ACTION_ADD, kCard1SyncTag, sync_pb::WalletMetadataSpecifics::CARD, @@ -867,11 +873,11 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, // data, which is used to avoid calling the expensive GetAllSyncData() function. TEST_F(AutofillWalletMetadataSyncableServiceTest, CacheIsUpToDateAfterSyncChange) { - local_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, true)); + local_.UpdateAddressStats(BuildAddress(kAddr1, 2, 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(kAddr1, 2, 2, true)); remote_.UpdateAddressStats(BuildAddress(kAddr2, 3, 4, false)); remote_.UpdateCardStats(BuildCard(kCard1, 5, 6, kAddr1)); remote_.UpdateCardStats(BuildCard(kCard2, 7, 8, kAddr2)); @@ -903,7 +909,7 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, // values to the sync server. TEST_F(AutofillWalletMetadataSyncableServiceTest, SaveHigherValuesLocallyOnLateDataArrival) { - remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, false)); + remote_.UpdateAddressStats(BuildAddress(kAddr1, 2, 2, false)); remote_.UpdateCardStats(BuildCard(kCard1, 5, 6, kAddr1)); MergeMetadata(&local_, &remote_); syncer::SyncChangeList changes; @@ -931,7 +937,7 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, // once the data finally arrives. TEST_F(AutofillWalletMetadataSyncableServiceTest, SaveHigherValuesLocallyOnLateDataArrivalAfterPartialUpdates) { - remote_.UpdateAddressStats(BuildAddress(kAddr1, 1, 2, false)); + remote_.UpdateAddressStats(BuildAddress(kAddr1, 2, 2, false)); remote_.UpdateAddressStats(BuildAddress(kAddr2, 3, 4, false)); remote_.UpdateCardStats(BuildCard(kCard1, 5, 6, kAddr1)); remote_.UpdateCardStats(BuildCard(kCard2, 7, 8, kAddr1)); @@ -974,7 +980,7 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, // 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_.UpdateAddressStats(BuildAddress(kAddr1, 2, 20, true)); local_.UpdateCardStats(BuildCard(kCard1, 30, 4, "")); remote_.UpdateAddressStats(BuildAddress(kAddr1, 10, 2, false)); remote_.UpdateCardStats(BuildCard(kCard1, 3, 40, kAddr1)); @@ -1025,9 +1031,10 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, SaveHigherValues_Mixed2) { } // 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. +// address id refering to a Wallet address, the one with the most recent +// (bigger) use date is kept. TEST_F(AutofillWalletMetadataSyncableServiceTest, - SaveHigherValues_DifferentBillingAddressId_LocalMostRecent) { + DifferentServerBillingAddressId_LocalMostRecent) { local_.UpdateCardStats(BuildCard(kCard1, 3, 40, kAddr1)); remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr2)); @@ -1044,9 +1051,10 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, } // 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. +// address id refering to a Wallet address, the one with the most recent +// (bigger) use date is kept. TEST_F(AutofillWalletMetadataSyncableServiceTest, - SaveHigherValues_DifferentBillingAddressId_RemoteMostRecent) { + DifferentServerBillingAddressId_RemoteMostRecent) { local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); remote_.UpdateCardStats(BuildCard(kCard1, 3, 40, kAddr2)); @@ -1059,5 +1067,169 @@ TEST_F(AutofillWalletMetadataSyncableServiceTest, MergeMetadata(&local_, &remote_); } +// Verify that if both local and server have a different non empty billing +// address id refering to a local profile, the one with the most recent (bigger) +// use date is kept. +TEST_F(AutofillWalletMetadataSyncableServiceTest, + DifferentLocalBillingAddressId_LocalMostRecent) { + local_.UpdateCardStats(BuildCard(kCard1, 3, 40, kLocalAddr1)); + remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kLocalAddr2)); + + // 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, kLocalAddr1Utf8)))); + + MergeMetadata(&local_, &remote_); +} + +// Verify that if both local and server have a different non empty billing +// address id refering to a local profile, the one with the most recent (bigger) +// use date is kept. +TEST_F(AutofillWalletMetadataSyncableServiceTest, + DifferentLocalBillingAddressId_RemoteMostRecent) { + local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kLocalAddr1)); + remote_.UpdateCardStats(BuildCard(kCard1, 3, 40, kLocalAddr2)); + + // The value from the remote should be kept because it has a more recent use + // date. + EXPECT_CALL(local_, UpdateCardStats(AutofillCardMetadataMatches( + kCard1, 3, 40, kLocalAddr2))); + EXPECT_CALL(local_, SendChangesToSyncServer(_)).Times(0); + + MergeMetadata(&local_, &remote_); +} + +// Verify that if both local and server have a different non empty billing +// address id, the one refering to a local profile is always kept. +TEST_F(AutofillWalletMetadataSyncableServiceTest, + DifferentBillingAddressId_KeepLocalId_Local) { + local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kLocalAddr1)); + remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr2)); + + // The billing address from the local version of the card should be kept since + // it refers to a local autofill profile. + EXPECT_CALL(local_, UpdateCardStats(_)).Times(0); + EXPECT_CALL(local_, SendChangesToSyncServer( + UnorderedElementsAre(SyncCardChangeAndDataMatch( + syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag, + sync_pb::WalletMetadataSpecifics::CARD, + kCard1Utf8, 3, 4, kLocalAddr1Utf8)))); + + MergeMetadata(&local_, &remote_); +} + +// Verify that if both local and server have a different non empty billing +// address id, the one refering to a local profile is always kept, even id the +// other was used more recently. +TEST_F(AutofillWalletMetadataSyncableServiceTest, + DifferentBillingAddressId_KeepLocalId_Remote) { + local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr2)); + remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kLocalAddr1)); + + // The billing address from the remote version of the card should be kept + // since it refers to a local autofill profile. + EXPECT_CALL(local_, UpdateCardStats(AutofillCardMetadataMatches( + kCard1, 3, 4, kLocalAddr1))); + EXPECT_CALL(local_, SendChangesToSyncServer(_)).Times(0); + + MergeMetadata(&local_, &remote_); +} + +// Verify that if both local and server have a different non empty billing +// address id, the one refering to a local profile is always kept, even id the +// other was used more recently. Also makes sure that for the rest of the fields +// the highest values are kept. +TEST_F(AutofillWalletMetadataSyncableServiceTest, + SaveHigherValues_DifferentBillingAddressId_KeepLocalId_Local) { + local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kLocalAddr1)); + remote_.UpdateCardStats(BuildCard(kCard1, 30, 40, kAddr2)); + + // The billing address from the local version of the card should be kept since + // it refers to a local autofill profile. The highest use stats should + // be kept. + EXPECT_CALL(local_, UpdateCardStats(AutofillCardMetadataMatches( + kCard1, 30, 40, kLocalAddr1))); + EXPECT_CALL(local_, SendChangesToSyncServer( + UnorderedElementsAre(SyncCardChangeAndDataMatch( + syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag, + sync_pb::WalletMetadataSpecifics::CARD, + kCard1Utf8, 30, 40, kLocalAddr1Utf8)))); + + MergeMetadata(&local_, &remote_); +} + +// Verify that if both local and server have a different non empty billing +// address id, the one refering to a local profile is always kept. Also makes +// sure that for the rest of the fields the highest values are kept. +TEST_F(AutofillWalletMetadataSyncableServiceTest, + SaveHigherValues_DifferentBillingAddressId_KeepLocalId_Remote) { + local_.UpdateCardStats(BuildCard(kCard1, 30, 40, kAddr2)); + remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kLocalAddr1)); + + // The billing address from the remote version of the card should be kept + // since it refers to a local autofill profile. The highest use stats should + // be kept. + EXPECT_CALL(local_, UpdateCardStats(AutofillCardMetadataMatches( + kCard1, 30, 40, kLocalAddr1))); + EXPECT_CALL(local_, SendChangesToSyncServer( + UnorderedElementsAre(SyncCardChangeAndDataMatch( + syncer::SyncChange::ACTION_UPDATE, kCard1SyncTag, + sync_pb::WalletMetadataSpecifics::CARD, + kCard1Utf8, 30, 40, kLocalAddr1Utf8)))); + + MergeMetadata(&local_, &remote_); +} + +// Verify that if both local and server have a different non empty billing +// address id refering to a Wallet address with the same timestamp, the remote +// one is kept. +TEST_F(AutofillWalletMetadataSyncableServiceTest, + DifferentServerBillingAddressId_BothSameTimestamp) { + local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr1)); + remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kAddr2)); + + // The value from the remote should be kept to promote a stable set of values. + EXPECT_CALL(local_, UpdateCardStats( + AutofillCardMetadataMatches(kCard1, 3, 4, kAddr2))); + EXPECT_CALL(local_, SendChangesToSyncServer(_)).Times(0); + + MergeMetadata(&local_, &remote_); +} + +// Verify that if both local and server have a different non empty billing +// address id refering to a local profile with the same timestamp, the remote +// one is kept. +TEST_F(AutofillWalletMetadataSyncableServiceTest, + DifferentLocalBillingAddressId_BothSameTimestamp) { + local_.UpdateCardStats(BuildCard(kCard1, 3, 4, kLocalAddr1)); + remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kLocalAddr2)); + + // The value from the remote should be kept to promote a stable set of values. + EXPECT_CALL(local_, UpdateCardStats(AutofillCardMetadataMatches( + kCard1, 3, 4, kLocalAddr2))); + EXPECT_CALL(local_, SendChangesToSyncServer(_)).Times(0); + + MergeMetadata(&local_, &remote_); +} + +// Verify that if the local card has a use_count of one, its use_date is +// replaced even if it is more recent (new cards are created with a use_date set +// to the current time). +TEST_F(AutofillWalletMetadataSyncableServiceTest, NewLocalCard) { + local_.UpdateCardStats(BuildCard(kCard1, 1, 5000, kLocalAddr1)); + remote_.UpdateCardStats(BuildCard(kCard1, 3, 4, kLocalAddr1)); + + EXPECT_CALL(local_, UpdateCardStats(AutofillCardMetadataMatches( + kCard1, 3, 4, kLocalAddr1))); + 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 8ad43337b41..b43f4fc3ed7 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 @@ -291,7 +291,7 @@ void AutofillWalletSyncableService::PopulateWalletCardsAndAddresses( } // static -void AutofillWalletSyncableService::CopyRelevantBillingAddressesFromDisk( +void AutofillWalletSyncableService::CopyRelevantMetadataFromDisk( const AutofillTable& table, std::vector<CreditCard>* cards_from_server) { std::vector<std::unique_ptr<CreditCard>> cards_on_disk; @@ -301,6 +301,11 @@ void AutofillWalletSyncableService::CopyRelevantBillingAddressesFromDisk( for (const auto& saved_card : cards_on_disk) { for (CreditCard& server_card : *cards_from_server) { if (saved_card->server_id() == server_card.server_id()) { + // The wallet data doesn't have the use stats. Use the ones present on + // disk to not overwrite them with bad data. + server_card.set_use_count(saved_card->use_count()); + server_card.set_use_date(saved_card->use_date()); + // 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) { @@ -320,11 +325,11 @@ syncer::SyncMergeResult AutofillWalletSyncableService::SetSyncData( // 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|. + // server. To preserve user's preferred billing address and most recent use + // stats, copy them from disk into |wallet_cards|. AutofillTable* table = AutofillTable::FromWebDatabase(webdata_backend_->GetDatabase()); - CopyRelevantBillingAddressesFromDisk(*table, &wallet_cards); + CopyRelevantMetadataFromDisk(*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 03f13116409..ae8bae82f66 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 @@ -61,15 +61,17 @@ class AutofillWalletSyncableService const std::string& app_locale); private: + FRIEND_TEST_ALL_PREFIXES(AutofillWalletSyncableServiceTest, + CopyRelevantMetadataFromDisk_KeepLocalAddresses); FRIEND_TEST_ALL_PREFIXES( AutofillWalletSyncableServiceTest, - CopyRelevantBillingAddressesFromDisk_KeepLocalAddresses); - FRIEND_TEST_ALL_PREFIXES( - AutofillWalletSyncableServiceTest, - CopyRelevantBillingAddressesFromDisk_OverwriteOtherAddresses); + CopyRelevantMetadataFromDisk_OverwriteOtherAddresses); FRIEND_TEST_ALL_PREFIXES( AutofillWalletSyncableServiceTest, PopulateWalletCardsAndAddresses_BillingAddressIdTransfer); + FRIEND_TEST_ALL_PREFIXES(AutofillWalletSyncableServiceTest, + CopyRelevantMetadataFromDisk_KeepUseStats); + FRIEND_TEST_ALL_PREFIXES(AutofillWalletSyncableServiceTest, NewWalletCard); syncer::SyncMergeResult SetSyncData(const syncer::SyncDataList& data_list); @@ -81,10 +83,10 @@ class AutofillWalletSyncableService 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( + // overwrites the server version with the use stats saved on disk, and the + // billing id if it refers to a local autofill profile. The credit card's IDs + // do not change over time. + static void CopyRelevantMetadataFromDisk( const AutofillTable& table, std::vector<CreditCard>* cards_from_server); 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 index 9561a8b1c9b..41e33728dee 100644 --- 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 @@ -9,6 +9,7 @@ #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/test_autofill_clock.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" @@ -99,7 +100,7 @@ TEST(AutofillWalletSyncableServiceTest, // 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) { + CopyRelevantMetadataFromDisk_KeepLocalAddresses) { std::vector<CreditCard> cards_on_disk; std::vector<CreditCard> wallet_cards; @@ -119,8 +120,8 @@ TEST(AutofillWalletSyncableServiceTest, // Setup the TestAutofillTable with the cards_on_disk. TestAutofillTable table(cards_on_disk); - AutofillWalletSyncableService::CopyRelevantBillingAddressesFromDisk( - table, &wallet_cards); + AutofillWalletSyncableService::CopyRelevantMetadataFromDisk(table, + &wallet_cards); ASSERT_EQ(1U, wallet_cards.size()); @@ -133,7 +134,7 @@ TEST(AutofillWalletSyncableServiceTest, // 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) { + CopyRelevantMetadataFromDisk_OverwriteOtherAddresses) { std::string old_billing_id = "1234"; std::string new_billing_id = "9876"; std::vector<CreditCard> cards_on_disk; @@ -152,8 +153,8 @@ TEST(AutofillWalletSyncableServiceTest, // Setup the TestAutofillTable with the cards_on_disk. TestAutofillTable table(cards_on_disk); - AutofillWalletSyncableService::CopyRelevantBillingAddressesFromDisk( - table, &wallet_cards); + AutofillWalletSyncableService::CopyRelevantMetadataFromDisk(table, + &wallet_cards); ASSERT_EQ(1U, wallet_cards.size()); @@ -162,4 +163,63 @@ TEST(AutofillWalletSyncableServiceTest, EXPECT_EQ(new_billing_id, wallet_cards.back().billing_address_id()); } +// Verify that the use stats on disk are kept when server cards are synced. +TEST(AutofillWalletSyncableServiceTest, + CopyRelevantMetadataFromDisk_KeepUseStats) { + TestAutofillClock test_clock; + base::Time arbitrary_time = base::Time::FromDoubleT(25); + base::Time disk_time = base::Time::FromDoubleT(10); + test_clock.SetNow(arbitrary_time); + + std::vector<CreditCard> cards_on_disk; + std::vector<CreditCard> wallet_cards; + + // Create a card on disk with specific use stats. + cards_on_disk.push_back(CreditCard()); + cards_on_disk.back().set_use_count(3U); + cards_on_disk.back().set_use_date(disk_time); + + // Create a card pulled from wallet with the same id, but a different billing + // address id. + wallet_cards.push_back(CreditCard()); + wallet_cards.back().set_use_count(10U); + + // Setup the TestAutofillTable with the cards_on_disk. + TestAutofillTable table(cards_on_disk); + + AutofillWalletSyncableService::CopyRelevantMetadataFromDisk(table, + &wallet_cards); + + ASSERT_EQ(1U, wallet_cards.size()); + + // Make sure the use stats from disk were kept + EXPECT_EQ(3U, wallet_cards.back().use_count()); + EXPECT_EQ(disk_time, wallet_cards.back().use_date()); +} + +// Verify that the use stats of a new Wallet card are as expected. +TEST(AutofillWalletSyncableServiceTest, NewWalletCard) { + TestAutofillClock test_clock; + base::Time arbitrary_time = base::Time::FromDoubleT(25); + test_clock.SetNow(arbitrary_time); + + std::vector<AutofillProfile> wallet_addresses; + std::vector<CreditCard> wallet_cards; + 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()); + + // The use_count should be 1 and the use_date should be the current time. + EXPECT_EQ(1U, wallet_cards.back().use_count()); + EXPECT_EQ(arbitrary_time, wallet_cards.back().use_date()); +} + } // namespace autofill
\ No newline at end of file diff --git a/chromium/components/autofill/core/browser/webdata/system_encryptor.cc b/chromium/components/autofill/core/browser/webdata/system_encryptor.cc new file mode 100644 index 00000000000..7b16c089529 --- /dev/null +++ b/chromium/components/autofill/core/browser/webdata/system_encryptor.cc @@ -0,0 +1,21 @@ +// 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/system_encryptor.h" + +#include "components/os_crypt/os_crypt.h" + +namespace autofill { + +bool SystemEncryptor::EncryptString16(const base::string16& plaintext, + std::string* ciphertext) const { + return ::OSCrypt::EncryptString16(plaintext, ciphertext); +} + +bool SystemEncryptor::DecryptString16(const std::string& ciphertext, + base::string16* plaintext) const { + return ::OSCrypt::DecryptString16(ciphertext, plaintext); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/webdata/system_encryptor.h b/chromium/components/autofill/core/browser/webdata/system_encryptor.h new file mode 100644 index 00000000000..e6efb426c63 --- /dev/null +++ b/chromium/components/autofill/core/browser/webdata/system_encryptor.h @@ -0,0 +1,30 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_SYSTEM_ENCRYPTOR_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_SYSTEM_ENCRYPTOR_H_ + +#include "base/macros.h" +#include "components/autofill/core/browser/webdata/autofill_table_encryptor.h" + +namespace autofill { +// Default encryptor used in Autofill table. +class SystemEncryptor : public AutofillTableEncryptor { + public: + SystemEncryptor() = default; + ~SystemEncryptor() override = default; + + bool EncryptString16(const base::string16& plaintext, + std::string* ciphertext) const override; + + bool DecryptString16(const std::string& ciphertext, + base::string16* plaintext) const override; + + private: + DISALLOW_COPY_AND_ASSIGN(SystemEncryptor); +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_SYSTEM_ENCRYPTOR_H_ diff --git a/chromium/components/autofill/core/browser/webdata/web_data_model_type_controller.cc b/chromium/components/autofill/core/browser/webdata/web_data_model_type_controller.cc new file mode 100644 index 00000000000..aa2eb36397d --- /dev/null +++ b/chromium/components/autofill/core/browser/webdata/web_data_model_type_controller.cc @@ -0,0 +1,36 @@ +// 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/web_data_model_type_controller.h" + +#include "base/bind.h" +#include "base/bind_helpers.h" + +using syncer::ModelType; +using syncer::ModelTypeController; +using syncer::SyncClient; + +namespace autofill { + +WebDataModelTypeController::WebDataModelTypeController( + ModelType type, + SyncClient* sync_client, + const scoped_refptr<base::SingleThreadTaskRunner>& model_thread, + const scoped_refptr<AutofillWebDataService>& web_data_service, + const BridgeFromWebData& bridge_from_web_data) + : ModelTypeController(type, sync_client, model_thread), + web_data_service_(web_data_service), + bridge_from_web_data_(bridge_from_web_data) {} + +WebDataModelTypeController::~WebDataModelTypeController() {} + +ModelTypeController::BridgeProvider +WebDataModelTypeController::GetBridgeProvider() { + // As opposed to the default implementation, get the bridge on demand, the web + // data service requires us to be on the model thread. + return base::Bind(bridge_from_web_data_, + base::RetainedRef(web_data_service_)); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/webdata/web_data_model_type_controller.h b/chromium/components/autofill/core/browser/webdata/web_data_model_type_controller.h new file mode 100644 index 00000000000..b071d28c556 --- /dev/null +++ b/chromium/components/autofill/core/browser/webdata/web_data_model_type_controller.h @@ -0,0 +1,49 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_WEB_DATA_MODEL_TYPE_CONTROLLER_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_WEB_DATA_MODEL_TYPE_CONTROLLER_H_ + +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "components/autofill/core/browser/webdata/autofill_webdata_service.h" +#include "components/sync/driver/model_type_controller.h" + +namespace syncer { +class ModelTypeSyncBridge; +} // namespace syncer + +namespace autofill { + +class WebDataModelTypeController : public syncer::ModelTypeController { + public: + using BridgeFromWebData = + base::Callback<base::WeakPtr<syncer::ModelTypeSyncBridge>( + AutofillWebDataService*)>; + + WebDataModelTypeController( + syncer::ModelType type, + syncer::SyncClient* sync_client, + const scoped_refptr<base::SingleThreadTaskRunner>& model_thread, + const scoped_refptr<AutofillWebDataService>& web_data_service, + const BridgeFromWebData& bridge_from_web_data); + + ~WebDataModelTypeController() override; + + private: + // syncer::ModelTypeController implementation. + syncer::ModelTypeController::BridgeProvider GetBridgeProvider() override; + + // A reference to the AutofillWebDataService for this controller. + scoped_refptr<AutofillWebDataService> web_data_service_; + + // How to grab the correct bridge from a web data. + BridgeFromWebData bridge_from_web_data_; + + DISALLOW_COPY_AND_ASSIGN(WebDataModelTypeController); +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_WEB_DATA_MODEL_TYPE_CONTROLLER_H_ diff --git a/chromium/components/autofill/core/common/BUILD.gn b/chromium/components/autofill/core/common/BUILD.gn index d01d18e3b28..f7a34bb28b6 100644 --- a/chromium/components/autofill/core/common/BUILD.gn +++ b/chromium/components/autofill/core/common/BUILD.gn @@ -4,6 +4,8 @@ static_library("common") { sources = [ + "autofill_clock.cc", + "autofill_clock.h", "autofill_constants.cc", "autofill_constants.h", "autofill_data_validation.cc", diff --git a/chromium/components/autofill/core/common/autofill_clock.cc b/chromium/components/autofill/core/common/autofill_clock.cc new file mode 100644 index 00000000000..8702403d9e5 --- /dev/null +++ b/chromium/components/autofill/core/common/autofill_clock.cc @@ -0,0 +1,43 @@ +// 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/common/autofill_clock.h" + +#include <utility> + +#include "base/time/clock.h" +#include "base/time/default_clock.h" + +namespace autofill { + +namespace { + +static base::LazyInstance<AutofillClock> g_autofill_clock = + LAZY_INSTANCE_INITIALIZER; + +} // namespace + +// static +base::Time AutofillClock::Now() { + if (!g_autofill_clock.Get().clock_) + SetClock(); + + return g_autofill_clock.Get().clock_->Now(); +} + +AutofillClock::AutofillClock(){}; +AutofillClock::~AutofillClock(){}; + +// static +void AutofillClock::SetClock() { + g_autofill_clock.Get().clock_.reset(new base::DefaultClock()); +} + +// static +void AutofillClock::SetTestClock(std::unique_ptr<base::Clock> clock) { + DCHECK(clock); + g_autofill_clock.Get().clock_ = std::move(clock); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/common/autofill_clock.h b/chromium/components/autofill/core/common/autofill_clock.h new file mode 100644 index 00000000000..45a991211ba --- /dev/null +++ b/chromium/components/autofill/core/common/autofill_clock.h @@ -0,0 +1,48 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_AUTOFILL_CORE_COMMON_AUTOFILL_CLOCK_H_ +#define COMPONENTS_AUTOFILL_CORE_COMMON_AUTOFILL_CLOCK_H_ + +#include <memory> + +#include "base/lazy_instance.h" +#include "base/macros.h" + +namespace base { +class Clock; +class Time; +} // namespace base + +namespace autofill { + +// Handles getting the current time for the Autofill feature. Can be injected +// with a customizable clock to facilitate testing. +class AutofillClock { + public: + // Returns the current time based on |clock_|. + static base::Time Now(); + + private: + friend class TestAutofillClock; + friend struct base::DefaultLazyInstanceTraits<AutofillClock>; + + // Resets a normal clock. + static void SetClock(); + + // Sets the clock to be used for tests. + static void SetTestClock(std::unique_ptr<base::Clock> clock); + + AutofillClock(); + ~AutofillClock(); + + // The clock used to return the current time. + std::unique_ptr<base::Clock> clock_; + + DISALLOW_COPY_AND_ASSIGN(AutofillClock); +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_COMMON_AUTOFILL_CLOCK_H_ diff --git a/chromium/components/autofill/core/common/autofill_switches.cc b/chromium/components/autofill/core/common/autofill_switches.cc index 8aadec54c62..217408a99d8 100644 --- a/chromium/components/autofill/core/common/autofill_switches.cc +++ b/chromium/components/autofill/core/common/autofill_switches.cc @@ -56,6 +56,9 @@ const char kLocalHeuristicsOnlyForPasswordGeneration[] = // Annotates forms with Autofill field type predictions. const char kShowAutofillTypePredictions[] = "show-autofill-type-predictions"; +// Annotates forms and fields with Autofill signatures. +const char kShowAutofillSignatures[] = "show-autofill-signatures"; + // Use the sandbox Online Wallet service URL (for developer testing). const char kWalletServiceUseSandbox[] = "wallet-service-use-sandbox"; diff --git a/chromium/components/autofill/core/common/autofill_switches.h b/chromium/components/autofill/core/common/autofill_switches.h index e423cca352b..b1c7d909c2b 100644 --- a/chromium/components/autofill/core/common/autofill_switches.h +++ b/chromium/components/autofill/core/common/autofill_switches.h @@ -24,6 +24,7 @@ extern const char kEnableSuggestionsWithSubstringMatch[]; extern const char kIgnoreAutocompleteOffForAutofill[]; extern const char kLocalHeuristicsOnlyForPasswordGeneration[]; extern const char kShowAutofillTypePredictions[]; +extern const char kShowAutofillSignatures[]; extern const char kWalletServiceUseSandbox[]; #if defined(OS_ANDROID) diff --git a/chromium/components/autofill/core/common/autofill_util.cc b/chromium/components/autofill/core/common/autofill_util.cc index bc458a9609c..c20ac562e43 100644 --- a/chromium/components/autofill/core/common/autofill_util.cc +++ b/chromium/components/autofill/core/common/autofill_util.cc @@ -42,6 +42,11 @@ bool IsFeatureSubstringMatchEnabled() { switches::kEnableSuggestionsWithSubstringMatch); } +bool IsShowAutofillSignaturesEnabled() { + return base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kShowAutofillSignatures); +} + bool IsKeyboardAccessoryEnabled() { #if defined(OS_ANDROID) return base::CommandLine::ForCurrentProcess()->HasSwitch( diff --git a/chromium/components/autofill/core/common/autofill_util.h b/chromium/components/autofill/core/common/autofill_util.h index fccd473af20..a9738774580 100644 --- a/chromium/components/autofill/core/common/autofill_util.h +++ b/chromium/components/autofill/core/common/autofill_util.h @@ -19,6 +19,9 @@ namespace autofill { // is on. bool IsFeatureSubstringMatchEnabled(); +// Returns true if showing autofill signature as HTML attributes is enabled. +bool IsShowAutofillSignaturesEnabled(); + // Returns true when keyboard accessory is enabled. bool IsKeyboardAccessoryEnabled(); diff --git a/chromium/components/autofill/core/common/form_field_data.cc b/chromium/components/autofill/core/common/form_field_data.cc index 6e19116ad9a..48796627fa9 100644 --- a/chromium/components/autofill/core/common/form_field_data.cc +++ b/chromium/components/autofill/core/common/form_field_data.cc @@ -15,7 +15,7 @@ namespace { // Increment this anytime pickle format is modified as well as provide // deserialization routine from previous kPickleVersion format. -const int kPickleVersion = 6; +const int kPickleVersion = 7; void AddVectorToPickle(std::vector<base::string16> strings, base::Pickle* pickle) { @@ -114,6 +114,11 @@ bool DeserializeSection9(base::PickleIterator* iter, return iter->ReadUInt32(&field_data->properties_mask); } +bool DeserializeSection10(base::PickleIterator* iter, + FormFieldData* field_data) { + return iter->ReadString16(&field_data->id); +} + } // namespace FormFieldData::FormFieldData() @@ -134,7 +139,7 @@ FormFieldData::~FormFieldData() { bool FormFieldData::SameFieldAs(const FormFieldData& field) const { // A FormFieldData stores a value, but the value is not part of the identity // of the field, so we don't want to compare the values. - return label == field.label && name == field.name && + return label == field.label && name == field.name && id == field.id && form_control_type == field.form_control_type && autocomplete_attribute == field.autocomplete_attribute && placeholder == field.placeholder && max_length == field.max_length && @@ -177,6 +182,8 @@ bool FormFieldData::operator<(const FormFieldData& field) const { if (label > field.label) return false; if (name < field.name) return true; if (name > field.name) return false; + if (id < field.id) return true; + if (id > field.id) return false; if (form_control_type < field.form_control_type) return true; if (form_control_type > field.form_control_type) return false; if (autocomplete_attribute < field.autocomplete_attribute) return true; @@ -222,6 +229,7 @@ void SerializeFormFieldData(const FormFieldData& field_data, pickle->WriteString16(field_data.placeholder); pickle->WriteString16(field_data.css_classes); pickle->WriteUInt32(field_data.properties_mask); + pickle->WriteString16(field_data.id); } bool DeserializeFormFieldData(base::PickleIterator* iter, @@ -306,6 +314,21 @@ bool DeserializeFormFieldData(base::PickleIterator* iter, } break; } + case 7: { + if (!DeserializeSection1(iter, &temp_form_field_data) || + !DeserializeSection6(iter, &temp_form_field_data) || + !DeserializeSection7(iter, &temp_form_field_data) || + !DeserializeSection2(iter, &temp_form_field_data) || + !DeserializeSection3(iter, &temp_form_field_data) || + !DeserializeSection4(iter, &temp_form_field_data) || + !DeserializeSection8(iter, &temp_form_field_data) || + !DeserializeSection9(iter, &temp_form_field_data) || + !DeserializeSection10(iter, &temp_form_field_data)) { + LOG(ERROR) << "Could not deserialize FormFieldData from pickle"; + return false; + } + break; + } default: { LOG(ERROR) << "Unknown FormFieldData pickle version " << version; return false; @@ -341,6 +364,7 @@ std::ostream& operator<<(std::ostream& os, const FormFieldData& field) { return os << base::UTF16ToUTF8(field.label) << " " << base::UTF16ToUTF8(field.name) << " " + << base::UTF16ToUTF8(field.id) << " " << base::UTF16ToUTF8(field.value) << " " << field.form_control_type << " " << field.autocomplete_attribute << " " << field.placeholder << " " << field.max_length << " " << field.css_classes << " " diff --git a/chromium/components/autofill/core/common/form_field_data.h b/chromium/components/autofill/core/common/form_field_data.h index dbcb3ea445e..a4de0a2236c 100644 --- a/chromium/components/autofill/core/common/form_field_data.h +++ b/chromium/components/autofill/core/common/form_field_data.h @@ -69,6 +69,7 @@ struct FormFieldData { // serializing functions (in the .cc file) and the constructor. base::string16 label; base::string16 name; + base::string16 id; base::string16 value; std::string form_control_type; std::string autocomplete_attribute; @@ -118,6 +119,7 @@ std::ostream& operator<<(std::ostream& os, const FormFieldData& field); EXPECT_EQ(expected.is_autofilled, actual.is_autofilled); \ EXPECT_EQ(expected.check_status, actual.check_status); \ EXPECT_EQ(expected.properties_mask, actual.properties_mask); \ + EXPECT_EQ(expected.id, actual.id); \ } while (0) } // namespace autofill diff --git a/chromium/components/autofill/core/common/form_field_data_unittest.cc b/chromium/components/autofill/core/common/form_field_data_unittest.cc index cae49ecb4df..0c94126a202 100644 --- a/chromium/components/autofill/core/common/form_field_data_unittest.cc +++ b/chromium/components/autofill/core/common/form_field_data_unittest.cc @@ -49,6 +49,10 @@ void FillVersion6Fields(FormFieldData* data) { FieldPropertiesFlags::USER_TYPED | FieldPropertiesFlags::HAD_FOCUS; } +void FillVersion7Fields(FormFieldData* data) { + data->id = base::ASCIIToUTF16("id"); +} + void WriteSection1(const FormFieldData& data, base::Pickle* pickle) { pickle->WriteString16(data.label); pickle->WriteString16(data.name); @@ -99,6 +103,10 @@ void WriteVersion6Specific(const FormFieldData& data, base::Pickle* pickle) { pickle->WriteUInt32(data.properties_mask); } +void WriteVersion7Specific(const FormFieldData& data, base::Pickle* pickle) { + pickle->WriteString16(data.id); +} + void SerializeInVersion1Format(const FormFieldData& data, base::Pickle* pickle) { WriteSection1(data, pickle); @@ -159,6 +167,19 @@ void SerializeInVersion6Format(const FormFieldData& data, WriteVersion6Specific(data, pickle); } +void SerializeInVersion7Format(const FormFieldData& data, + base::Pickle* pickle) { + WriteSection1(data, pickle); + WriteSection4(data, pickle); + WriteSection5(data, pickle); + WriteVersion2Specific(data, pickle); + WriteSection2(data, pickle); + WriteVersion3Specific(data, pickle); + WriteVersion5Specific(data, pickle); + WriteVersion6Specific(data, pickle); + WriteVersion7Specific(data, pickle); +} + } // namespace TEST(FormFieldDataTest, SerializeAndDeserialize) { @@ -168,6 +189,7 @@ TEST(FormFieldDataTest, SerializeAndDeserialize) { FillVersion3Fields(&data); FillVersion5Fields(&data); FillVersion6Fields(&data); + FillVersion7Fields(&data); base::Pickle pickle; SerializeFormFieldData(data, &pickle); @@ -281,6 +303,26 @@ TEST(FormFieldDataTest, DeserializeVersion6) { EXPECT_TRUE(actual.SameFieldAs(data)); } +TEST(FormFieldDataTest, DeserializeVersion7) { + FormFieldData data; + FillCommonFields(&data); + FillVersion2Fields(&data); + FillVersion3Fields(&data); + FillVersion5Fields(&data); + FillVersion6Fields(&data); + FillVersion7Fields(&data); + + base::Pickle pickle; + pickle.WriteInt(7); + SerializeInVersion7Format(data, &pickle); + + base::PickleIterator iter(pickle); + FormFieldData actual; + EXPECT_TRUE(DeserializeFormFieldData(&iter, &actual)); + + EXPECT_TRUE(actual.SameFieldAs(data)); +} + // Verify that if the data isn't valid, the FormFieldData isn't populated // during deserialization. TEST(FormFieldDataTest, DeserializeBadData) { 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 93d1bc6c500..5fc676c20e9 100644 --- a/chromium/components/autofill/core/common/save_password_progress_logger.cc +++ b/chromium/components/autofill/core/common/save_password_progress_logger.cc @@ -18,7 +18,6 @@ using base::checked_cast; using base::Value; using base::DictionaryValue; -using base::FundamentalValue; using base::StringValue; namespace autofill { @@ -101,13 +100,13 @@ void SavePasswordProgressLogger::LogURL( void SavePasswordProgressLogger::LogBoolean( SavePasswordProgressLogger::StringID label, bool truth_value) { - LogValue(label, FundamentalValue(truth_value)); + LogValue(label, Value(truth_value)); } void SavePasswordProgressLogger::LogNumber( SavePasswordProgressLogger::StringID label, int signed_number) { - LogValue(label, FundamentalValue(signed_number)); + LogValue(label, Value(signed_number)); } void SavePasswordProgressLogger::LogNumber( diff --git a/chromium/components/autofill/ios/browser/autofill_driver_ios.mm b/chromium/components/autofill/ios/browser/autofill_driver_ios.mm index f867cd65182..08384ae36ad 100644 --- a/chromium/components/autofill/ios/browser/autofill_driver_ios.mm +++ b/chromium/components/autofill/ios/browser/autofill_driver_ios.mm @@ -6,6 +6,7 @@ #include "components/autofill/ios/browser/autofill_driver_ios_bridge.h" #include "ios/web/public/browser_state.h" +#import "ios/web/public/origin_util.h" #include "ios/web/public/web_state/web_state.h" #include "ios/web/public/web_thread.h" #include "ui/gfx/geometry/rect_f.h" @@ -82,7 +83,10 @@ void AutofillDriverIOS::RendererShouldAcceptDataListSuggestion( const base::string16& value) { } -void AutofillDriverIOS::DidInteractWithCreditCardForm() {} +void AutofillDriverIOS::DidInteractWithCreditCardForm() { + if (!web::IsOriginSecure(web_state_->GetLastCommittedURL())) + web_state_->OnCreditCardInputShownOnHttp(); +} void AutofillDriverIOS::RendererShouldClearFilledForm() { } |