diff options
author | erikchen@chromium.org <erikchen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-12 12:49:15 +0000 |
---|---|---|
committer | erikchen@chromium.org <erikchen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-12 12:49:15 +0000 |
commit | 8527a78f1482a20d3e4deef87f97335d4a2ff565 (patch) | |
tree | 2451be5591918455145cf2ceff9ad3f9a0411256 /components/autofill | |
parent | 30ea1296c8d4958df079a9232a4516bc8aad266e (diff) | |
download | chromium_src-8527a78f1482a20d3e4deef87f97335d4a2ff565.zip chromium_src-8527a78f1482a20d3e4deef87f97335d4a2ff565.tar.gz chromium_src-8527a78f1482a20d3e4deef87f97335d4a2ff565.tar.bz2 |
Revert "Revert 3 mac autofill CLs."
The reverted CLs are no longer suspected of causing a renderer crash.
https://code.google.com/p/chromium/issues/detail?id=382144
This reverts commit dbf46869b07c09eb74515ba2c8e9a727b0a09c65.
TBR=isherman@chromium.org
BUG=382144
Review URL: https://codereview.chromium.org/329293002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@276643 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'components/autofill')
20 files changed, 346 insertions, 41 deletions
diff --git a/components/autofill/content/browser/content_autofill_driver.cc b/components/autofill/content/browser/content_autofill_driver.cc index af74877..6707177 100644 --- a/components/autofill/content/browser/content_autofill_driver.cc +++ b/components/autofill/content/browser/content_autofill_driver.cc @@ -108,6 +108,13 @@ void ContentAutofillDriver::SendFormDataToRenderer( } } +void ContentAutofillDriver::PingRenderer() { + if (!RendererIsAvailable()) + return; + content::RenderViewHost* host = web_contents()->GetRenderViewHost(); + host->Send(new AutofillMsg_Ping(host->GetRoutingID())); +} + void ContentAutofillDriver::SendAutofillTypePredictionsToRenderer( const std::vector<FormStructure*>& forms) { if (!CommandLine::ForCurrentProcess()->HasSwitch( @@ -181,6 +188,9 @@ bool ContentAutofillDriver::OnMessageReceived(const IPC::Message& message) { IPC_MESSAGE_FORWARD(AutofillHostMsg_DidPreviewAutofillFormData, autofill_manager_.get(), AutofillManager::OnDidPreviewAutofillFormData) + IPC_MESSAGE_FORWARD(AutofillHostMsg_PingAck, + &autofill_external_delegate_, + AutofillExternalDelegate::OnPingAck) IPC_MESSAGE_FORWARD(AutofillHostMsg_DidFillAutofillFormData, autofill_manager_.get(), AutofillManager::OnDidFillAutofillFormData) diff --git a/components/autofill/content/browser/content_autofill_driver.h b/components/autofill/content/browser/content_autofill_driver.h index 0bb6934..e0294f5 100644 --- a/components/autofill/content/browser/content_autofill_driver.h +++ b/components/autofill/content/browser/content_autofill_driver.h @@ -50,6 +50,7 @@ class ContentAutofillDriver : public AutofillDriver, virtual void SendFormDataToRenderer(int query_id, RendererFormDataAction action, const FormData& data) OVERRIDE; + virtual void PingRenderer() OVERRIDE; virtual void SendAutofillTypePredictionsToRenderer( const std::vector<FormStructure*>& forms) OVERRIDE; virtual void RendererShouldAcceptDataListSuggestion( diff --git a/components/autofill/content/common/autofill_messages.h b/components/autofill/content/common/autofill_messages.h index cb9fc6a..f9a99fe 100644 --- a/components/autofill/content/common/autofill_messages.h +++ b/components/autofill/content/common/autofill_messages.h @@ -94,6 +94,11 @@ IPC_ENUM_TRAITS_MAX_VALUE( // Autofill messages sent from the browser to the renderer. +// Instructs the renderer to immediately return an IPC acknowledging the ping. +// This is used to correctly sequence events, since IPCs are guaranteed to be +// processed in order. +IPC_MESSAGE_ROUTED0(AutofillMsg_Ping) + // Instructs the renderer to fill the active form with the given form data. IPC_MESSAGE_ROUTED2(AutofillMsg_FillForm, int /* query_id */, @@ -227,6 +232,9 @@ IPC_MESSAGE_ROUTED5(AutofillHostMsg_QueryFormFieldAutofill, // Sent when a form is previewed with Autofill suggestions. IPC_MESSAGE_ROUTED0(AutofillHostMsg_DidPreviewAutofillFormData) +// Sent immediately after the renderer receives a ping IPC. +IPC_MESSAGE_ROUTED0(AutofillHostMsg_PingAck) + // Sent when a form is filled with Autofill suggestions. IPC_MESSAGE_ROUTED1(AutofillHostMsg_DidFillAutofillFormData, base::TimeTicks /* timestamp */) diff --git a/components/autofill/content/renderer/autofill_agent.cc b/components/autofill/content/renderer/autofill_agent.cc index 6d48fb5..525bc3f 100644 --- a/components/autofill/content/renderer/autofill_agent.cc +++ b/components/autofill/content/renderer/autofill_agent.cc @@ -150,6 +150,7 @@ AutofillAgent::~AutofillAgent() {} bool AutofillAgent::OnMessageReceived(const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(AutofillAgent, message) + IPC_MESSAGE_HANDLER(AutofillMsg_Ping, OnPing) IPC_MESSAGE_HANDLER(AutofillMsg_FillForm, OnFillForm) IPC_MESSAGE_HANDLER(AutofillMsg_PreviewForm, OnPreviewForm) IPC_MESSAGE_HANDLER(AutofillMsg_FieldTypePredictionsAvailable, @@ -455,6 +456,10 @@ void AutofillAgent::OnFillForm(int query_id, const FormData& form) { base::TimeTicks::Now())); } +void AutofillAgent::OnPing() { + Send(new AutofillHostMsg_PingAck(routing_id())); +} + void AutofillAgent::OnPreviewForm(int query_id, const FormData& form) { if (!render_view()->GetWebView() || query_id != autofill_query_id_) return; diff --git a/components/autofill/content/renderer/autofill_agent.h b/components/autofill/content/renderer/autofill_agent.h index 54a7c46..dafcd70 100644 --- a/components/autofill/content/renderer/autofill_agent.h +++ b/components/autofill/content/renderer/autofill_agent.h @@ -97,6 +97,7 @@ class AutofillAgent : public content::RenderViewObserver, void OnFieldTypePredictionsAvailable( const std::vector<FormDataPredictions>& forms); void OnFillForm(int query_id, const FormData& form); + void OnPing(); void OnPreviewForm(int query_id, const FormData& form); // For external Autofill selection. diff --git a/components/autofill/core/browser/autofill_driver.h b/components/autofill/core/browser/autofill_driver.h index efc5696..748ad6e 100644 --- a/components/autofill/core/browser/autofill_driver.h +++ b/components/autofill/core/browser/autofill_driver.h @@ -58,6 +58,9 @@ class AutofillDriver { RendererFormDataAction action, const FormData& data) = 0; + // Pings renderer. The renderer will return an IPC acknowledging the ping. + virtual void PingRenderer() = 0; + // Sends the field type predictions specified in |forms| to the renderer. This // method is a no-op if the renderer is not available or the appropriate // command-line flag is not set. @@ -81,7 +84,6 @@ class AutofillDriver { // Tells the renderer to preview the node with suggested text. virtual void RendererShouldPreviewFieldWithValue( const base::string16& value) = 0; - }; } // namespace autofill diff --git a/components/autofill/core/browser/autofill_external_delegate.cc b/components/autofill/core/browser/autofill_external_delegate.cc index bd66234..57777c8 100644 --- a/components/autofill/core/browser/autofill_external_delegate.cc +++ b/components/autofill/core/browser/autofill_external_delegate.cc @@ -4,6 +4,8 @@ #include "components/autofill/core/browser/autofill_external_delegate.h" +#include "base/message_loop/message_loop.h" +#include "base/metrics/histogram.h" #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/autocomplete_history_manager.h" #include "components/autofill/core/browser/autofill_driver.h" @@ -12,11 +14,35 @@ #include "grit/components_strings.h" #include "ui/base/l10n/l10n_util.h" +#if defined(OS_MACOSX) && !defined(OS_IOS) +namespace { + +enum AccessAddressBookEventType { + // An Autofill entry was shown that prompts the user to give Chrome access to + // the user's Address Book. + SHOWED_ACCESS_ADDRESS_BOOK_ENTRY = 0, + + // The user selected the Autofill entry which prompts Chrome to access the + // user's Address Book. + SELECTED_ACCESS_ADDRESS_BOOK_ENTRY = 1, + + // Always keep this at the end. + ACCESS_ADDRESS_BOOK_ENTRY_MAX, +}; + +// Emits an entry for the histogram. +void EmitHistogram(AccessAddressBookEventType type) { + UMA_HISTOGRAM_ENUMERATION( + "Autofill.MacAddressBook", type, ACCESS_ADDRESS_BOOK_ENTRY_MAX); +} + +} // namespace +#endif // defined(OS_MACOSX) && !defined(OS_IOS) + namespace autofill { -AutofillExternalDelegate::AutofillExternalDelegate( - AutofillManager* manager, - AutofillDriver* driver) +AutofillExternalDelegate::AutofillExternalDelegate(AutofillManager* manager, + AutofillDriver* driver) : manager_(manager), driver_(driver), query_id_(0), @@ -91,6 +117,20 @@ void AutofillExternalDelegate::OnSuggestionsReturned( // updated to match. InsertDataListValues(&values, &labels, &icons, &ids); +#if defined(OS_MACOSX) && !defined(OS_IOS) + if (values.empty() && + manager_->ShouldShowAccessAddressBookSuggestion(query_form_, + query_field_)) { + values.push_back( + l10n_util::GetStringUTF16(IDS_AUTOFILL_ACCESS_MAC_CONTACTS)); + labels.push_back(base::string16()); + icons.push_back(base::ASCIIToUTF16("macContactsIcon")); + ids.push_back(POPUP_ITEM_ID_MAC_ACCESS_CONTACTS); + + EmitHistogram(SHOWED_ACCESS_ADDRESS_BOOK_ENTRY); + } +#endif // defined(OS_MACOSX) && !defined(OS_IOS) + if (values.empty()) { // No suggestions, any popup currently showing is obsolete. manager_->client()->HideAutofillPopup(); @@ -155,6 +195,40 @@ void AutofillExternalDelegate::DidAcceptSuggestion(const base::string16& value, } else if (identifier == POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY) { // User selected an Autocomplete, so we fill directly. driver_->RendererShouldFillFieldWithValue(value); + } else if (identifier == POPUP_ITEM_ID_MAC_ACCESS_CONTACTS) { +#if defined(OS_MACOSX) && !defined(OS_IOS) + EmitHistogram(SELECTED_ACCESS_ADDRESS_BOOK_ENTRY); + + // User wants to give Chrome access to user's address book. + manager_->AccessAddressBook(); + + // There is no deterministic method for deciding whether a blocking dialog + // was presented. The following comments and code assume that a blocking + // dialog was presented, but still behave correctly if no dialog was + // presented. + + // A blocking dialog was presented, and the user has already responded to + // the dialog. The presentation of the dialog added an NSEvent to the + // NSRunLoop which will cause all windows to lose focus. When the NSEvent + // is processed, it will be sent to the renderer which will cause the text + // field to lose focus. This returns an IPC to Chrome which will dismiss + // the Autofill popup. We post a task which we expect to run after the + // NSEvent has been processed by the NSRunLoop. It pings the renderer, + // which returns an IPC acknowledging the ping. At that time, redisplay + // the popup. FIFO processing of IPCs ensures that all side effects of the + // NSEvent will have been processed. + + // 10ms sits nicely under the 16ms threshold for 60 fps, and likely gives + // the NSApplication run loop sufficient time to process the NSEvent. In + // testing, a delay of 0ms was always sufficient. + base::TimeDelta delay(base::TimeDelta::FromMilliseconds(10)); + base::MessageLoop::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&AutofillExternalDelegate::PingRenderer, GetWeakPtr()), + delay); +#else + NOTREACHED(); +#endif // defined(OS_MACOSX) && !defined(OS_IOS) } else { FillAutofillFormData(identifier, false); } @@ -184,6 +258,15 @@ void AutofillExternalDelegate::Reset() { manager_->client()->HideAutofillPopup(); } +void AutofillExternalDelegate::OnPingAck() { + // Reissue the most recent query, which will reopen the Autofill popup. + manager_->OnQueryFormFieldAutofill(query_id_, + query_form_, + query_field_, + element_bounds_, + display_warning_if_disabled_); +} + base::WeakPtr<AutofillExternalDelegate> AutofillExternalDelegate::GetWeakPtr() { return weak_ptr_factory_.GetWeakPtr(); } @@ -217,7 +300,7 @@ void AutofillExternalDelegate::ApplyAutofillWarnings( // suggestions to show, show a warning instead. Otherwise, clear out the // list of suggestions. if (!unique_ids->empty() && (*unique_ids)[0] > 0) { - // If autofill is disabled and we had suggestions, show a warning instead. + // If Autofill is disabled and we had suggestions, show a warning instead. values->assign( 1, l10n_util::GetStringUTF16(IDS_AUTOFILL_WARNING_FORM_DISABLED)); labels->assign(1, base::string16()); @@ -231,8 +314,8 @@ void AutofillExternalDelegate::ApplyAutofillWarnings( } } else if (unique_ids->size() > 1 && (*unique_ids)[0] == POPUP_ITEM_ID_WARNING_MESSAGE) { - // If we received a warning instead of suggestions from autofill but regular - // suggestions from autocomplete, don't show the autofill warning. + // If we received a warning instead of suggestions from Autofill but regular + // suggestions from autocomplete, don't show the Autofill warning. values->erase(values->begin()); labels->erase(labels->begin()); icons->erase(icons->begin()); @@ -306,4 +389,10 @@ void AutofillExternalDelegate::InsertDataListValues( POPUP_ITEM_ID_DATALIST_ENTRY); } +#if defined(OS_MACOSX) && !defined(OS_IOS) +void AutofillExternalDelegate::PingRenderer() { + driver_->PingRenderer(); +} +#endif // defined(OS_MACOSX) && !defined(OS_IOS) + } // namespace autofill diff --git a/components/autofill/core/browser/autofill_external_delegate.h b/components/autofill/core/browser/autofill_external_delegate.h index 672b2d6..b1e4111 100644 --- a/components/autofill/core/browser/autofill_external_delegate.h +++ b/components/autofill/core/browser/autofill_external_delegate.h @@ -80,6 +80,9 @@ class AutofillExternalDelegate // values or settings. void Reset(); + // The renderer sent an IPC acknowledging an earlier ping IPC. + void OnPingAck(); + protected: base::WeakPtr<AutofillExternalDelegate> GetWeakPtr(); @@ -111,6 +114,11 @@ class AutofillExternalDelegate std::vector<base::string16>* icons, std::vector<int>* unique_ids); +#if defined(OS_MACOSX) && !defined(OS_IOS) + // Pings the renderer. + void PingRenderer(); +#endif // defined(OS_MACOSX) && !defined(OS_IOS) + AutofillManager* manager_; // weak. // Provides driver-level context to the shared code of the component. Must diff --git a/components/autofill/core/browser/autofill_manager.cc b/components/autofill/core/browser/autofill_manager.cc index 50ed127..370a8f0 100644 --- a/components/autofill/core/browser/autofill_manager.cc +++ b/components/autofill/core/browser/autofill_manager.cc @@ -212,7 +212,7 @@ void AutofillManager::RegisterProfilePrefs( #endif // defined(OS_MACOSX) || defined(OS_ANDROID) #if defined(OS_MACOSX) registry->RegisterBooleanPref( - prefs::kAutofillAuxiliaryProfilesQueried, + prefs::kAutofillMacAddressBookQueried, false, user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); #endif // defined(OS_MACOSX) @@ -224,7 +224,50 @@ void AutofillManager::RegisterProfilePrefs( prefs::kAutofillNegativeUploadRate, kAutofillNegativeUploadRateDefaultValue, user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + +#if defined(OS_MACOSX) && !defined(OS_IOS) + registry->RegisterBooleanPref( + prefs::kAutofillUseMacAddressBook, + false, + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); +#endif // defined(OS_MACOSX) && !defined(OS_IOS) +} + +#if defined(OS_MACOSX) && !defined(OS_IOS) +void AutofillManager::MigrateUserPrefs(PrefService* prefs) { + const PrefService::Preference* pref = + prefs->FindPreference(prefs::kAutofillUseMacAddressBook); + + // If the pref is not its default value, then the migration has already been + // performed. + if (!pref->IsDefaultValue()) + return; + + // Whether Chrome has already tried to access the user's Address Book. + const PrefService::Preference* pref_accessed = + prefs->FindPreference(prefs::kAutofillMacAddressBookQueried); + // Whether the user wants to use the Address Book to populate Autofill. + const PrefService::Preference* pref_enabled = + prefs->FindPreference(prefs::kAutofillAuxiliaryProfilesEnabled); + + if (pref_accessed->IsDefaultValue() && pref_enabled->IsDefaultValue()) { + // This is likely a new user. Reset the default value to prevent the + // migration from happening again. + prefs->SetBoolean(prefs::kAutofillUseMacAddressBook, + prefs->GetBoolean(prefs::kAutofillUseMacAddressBook)); + return; + } + + bool accessed; + bool enabled; + bool success = pref_accessed->GetValue()->GetAsBoolean(&accessed); + DCHECK(success); + success = pref_enabled->GetValue()->GetAsBoolean(&enabled); + DCHECK(success); + + prefs->SetBoolean(prefs::kAutofillUseMacAddressBook, accessed && enabled); } +#endif // defined(OS_MACOSX) && !defined(OS_IOS) void AutofillManager::SetExternalDelegate(AutofillExternalDelegate* delegate) { // TODO(jrg): consider passing delegate into the ctor. That won't @@ -238,6 +281,28 @@ void AutofillManager::ShowAutofillSettings() { client_->ShowAutofillSettings(); } +#if defined(OS_MACOSX) && !defined(OS_IOS) +bool AutofillManager::ShouldShowAccessAddressBookSuggestion( + const FormData& form, + const FormFieldData& field) { + if (!personal_data_) + return false; + FormStructure* form_structure = NULL; + AutofillField* autofill_field = NULL; + if (!GetCachedFormAndField(form, field, &form_structure, &autofill_field)) + return false; + + return personal_data_->ShouldShowAccessAddressBookSuggestion( + autofill_field->Type()); +} + +bool AutofillManager::AccessAddressBook() { + if (!personal_data_) + return false; + return personal_data_->AccessAddressBook(); +} +#endif // defined(OS_MACOSX) && !defined(OS_IOS) + bool AutofillManager::OnFormSubmitted(const FormData& form, const TimeTicks& timestamp) { if (!IsValidFormData(form)) diff --git a/components/autofill/core/browser/autofill_manager.h b/components/autofill/core/browser/autofill_manager.h index b179606..120b14d 100644 --- a/components/autofill/core/browser/autofill_manager.h +++ b/components/autofill/core/browser/autofill_manager.h @@ -73,6 +73,10 @@ class AutofillManager : public AutofillDownloadManager::Observer { // Registers our Enable/Disable Autofill pref. static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry); +#if defined(OS_MACOSX) && !defined(OS_IOS) + static void MigrateUserPrefs(PrefService* prefs); +#endif // defined(OS_MACOSX) && !defined(OS_IOS) + AutofillManager(AutofillDriver* driver, AutofillClient* client, const std::string& app_locale, @@ -84,6 +88,19 @@ class AutofillManager : public AutofillDownloadManager::Observer { void ShowAutofillSettings(); +#if defined(OS_MACOSX) && !defined(OS_IOS) + // Whether the field represented by |fieldData| should show an entry to prompt + // the user to give Chrome access to the user's address book. + bool ShouldShowAccessAddressBookSuggestion(const FormData& data, + const FormFieldData& field_data); + + // If Chrome has not prompted for access to the user's address book, the + // method prompts the user for permission and blocks the process. Otherwise, + // this method has no effect. The return value reflects whether the user was + // prompted with a modal dialog. + bool AccessAddressBook(); +#endif // defined(OS_MACOSX) && !defined(OS_IOS) + // Called from our external delegate so they cannot be private. virtual void FillOrPreviewForm(AutofillDriver::RendererFormDataAction action, int query_id, diff --git a/components/autofill/core/browser/autofill_manager_unittest.cc b/components/autofill/core/browser/autofill_manager_unittest.cc index 731e58a..a8d3573 100644 --- a/components/autofill/core/browser/autofill_manager_unittest.cc +++ b/components/autofill/core/browser/autofill_manager_unittest.cc @@ -1551,8 +1551,14 @@ TEST_F(AutofillManagerTest, FillAddressForm) { // Test that we correctly fill an address form from an auxiliary profile. TEST_F(AutofillManagerTest, FillAddressFormFromAuxiliaryProfile) { personal_data_.ClearAutofillProfiles(); +#if defined(OS_MACOSX) && !defined(OS_IOS) + autofill_client_.GetPrefs()->SetBoolean( + ::autofill::prefs::kAutofillUseMacAddressBook, true); +#else autofill_client_.GetPrefs()->SetBoolean( ::autofill::prefs::kAutofillAuxiliaryProfilesEnabled, true); +#endif // defined(OS_MACOSX) && !defined(OS_IOS) + personal_data_.CreateTestAuxiliaryProfiles(); // Set up our form data. @@ -2474,12 +2480,12 @@ TEST_F(AutofillManagerTest, AuxiliaryProfilesReset) { PrefService* prefs = autofill_client_.GetPrefs(); #if defined(OS_MACOSX) || defined(OS_ANDROID) // Auxiliary profiles is implemented on Mac and Android only. - // OSX: enables Mac Address Book integration. + // OSX: This preference exists for legacy reasons. It is no longer used. // Android: enables integration with user's contact profile. ASSERT_TRUE( prefs->GetBoolean(::autofill::prefs::kAutofillAuxiliaryProfilesEnabled)); - prefs->SetBoolean( - ::autofill::prefs::kAutofillAuxiliaryProfilesEnabled, false); + prefs->SetBoolean(::autofill::prefs::kAutofillAuxiliaryProfilesEnabled, + false); prefs->ClearPref(::autofill::prefs::kAutofillAuxiliaryProfilesEnabled); ASSERT_TRUE( prefs->GetBoolean(::autofill::prefs::kAutofillAuxiliaryProfilesEnabled)); @@ -2490,7 +2496,7 @@ TEST_F(AutofillManagerTest, AuxiliaryProfilesReset) { prefs->ClearPref(::autofill::prefs::kAutofillAuxiliaryProfilesEnabled); ASSERT_FALSE( prefs->GetBoolean(::autofill::prefs::kAutofillAuxiliaryProfilesEnabled)); -#endif +#endif // defined(OS_MACOSX) || defined(OS_ANDROID) } TEST_F(AutofillManagerTest, DeterminePossibleFieldTypesForUpload) { diff --git a/components/autofill/core/browser/autofill_test_utils.cc b/components/autofill/core/browser/autofill_test_utils.cc index 081defa..0393d86 100644 --- a/components/autofill/core/browser/autofill_test_utils.cc +++ b/components/autofill/core/browser/autofill_test_utils.cc @@ -206,12 +206,17 @@ void DisableSystemServices(PrefService* prefs) { // Use a mock Keychain rather than the OS one to store credit card data. #if defined(OS_MACOSX) OSCrypt::UseMockKeychain(true); -#endif +#endif // defined(OS_MACOSX) - // Disable auxiliary profiles for unit testing. These reach out to system - // services on the Mac. +#if defined(OS_MACOSX) && !defined(OS_IOS) + // Don't use the Address Book on Mac, as it reaches out to system services. + if (prefs) + prefs->SetBoolean(prefs::kAutofillUseMacAddressBook, false); +#else + // Disable auxiliary profiles for unit testing by default. if (prefs) prefs->SetBoolean(prefs::kAutofillAuxiliaryProfilesEnabled, false); +#endif // defined(OS_MACOSX) && !defined(OS_IOS) } } // namespace test diff --git a/components/autofill/core/browser/personal_data_manager.cc b/components/autofill/core/browser/personal_data_manager.cc index 1d4173e..5454b1c 100644 --- a/components/autofill/core/browser/personal_data_manager.cc +++ b/components/autofill/core/browser/personal_data_manager.cc @@ -525,9 +525,13 @@ bool PersonalDataManager::IsDataLoaded() const { } const std::vector<AutofillProfile*>& PersonalDataManager::GetProfiles() const { - if (!pref_service_->GetBoolean(prefs::kAutofillAuxiliaryProfilesEnabled)) { +#if defined(OS_MACOSX) && !defined(OS_IOS) + if (!pref_service_->GetBoolean(prefs::kAutofillUseMacAddressBook)) return web_profiles(); - } +#else + if (!pref_service_->GetBoolean(prefs::kAutofillAuxiliaryProfilesEnabled)) + return web_profiles(); +#endif // defined(OS_MACOSX) && !defined(OS_IOS) profiles_.clear(); diff --git a/components/autofill/core/browser/personal_data_manager.h b/components/autofill/core/browser/personal_data_manager.h index 9ddc98e..e82e1a7 100644 --- a/components/autofill/core/browser/personal_data_manager.h +++ b/components/autofill/core/browser/personal_data_manager.h @@ -200,6 +200,18 @@ class PersonalDataManager : public KeyedService, // will only update when Chrome is restarted. virtual const std::string& GetDefaultCountryCodeForNewAddress() const; +#if defined(OS_MACOSX) && !defined(OS_IOS) + // If Chrome has not prompted for access to the user's address book, the + // method prompts the user for permission and blocks the process. Otherwise, + // this method has no effect. The return value reflects whether the user was + // prompted with a modal dialog. + bool AccessAddressBook(); + + // Whether an autofill suggestion should be displayed to prompt the user to + // grant Chrome access to the user's address book. + bool ShouldShowAccessAddressBookSuggestion(AutofillType type); +#endif // defined(OS_MACOSX) && !defined(OS_IOS) + protected: // Only PersonalDataManagerFactory and certain tests can create instances of // PersonalDataManager. diff --git a/components/autofill/core/browser/personal_data_manager_mac.mm b/components/autofill/core/browser/personal_data_manager_mac.mm index 8cae978..2be22fc 100644 --- a/components/autofill/core/browser/personal_data_manager_mac.mm +++ b/components/autofill/core/browser/personal_data_manager_mac.mm @@ -21,8 +21,10 @@ #include "components/autofill/core/browser/autofill_country.h" #include "components/autofill/core/browser/autofill_profile.h" #include "components/autofill/core/browser/autofill_type.h" +#include "components/autofill/core/browser/form_structure.h" #include "components/autofill/core/browser/phone_number.h" #include "components/autofill/core/common/autofill_pref_names.h" +#include "components/autofill/core/common/form_data.h" #include "grit/components_strings.h" #include "ui/base/l10n/l10n_util_mac.h" @@ -31,6 +33,39 @@ namespace { const char kAddressBookOrigin[] = "OS X Address Book"; +// Whether Chrome has prompted the user for permission to access the user's +// address book. +bool HasPromptedForAccessToAddressBook(PrefService* pref_service) { + return pref_service->GetBoolean(prefs::kAutofillMacAddressBookQueried); +} + +// Whether the user wants Chrome to use the AddressBook to populate Autofill +// entries. +bool ShouldUseAddressBook(PrefService* pref_service) { + return pref_service->GetBoolean(prefs::kAutofillUseMacAddressBook); +} + +ABAddressBook* GetAddressBook(PrefService* pref_service) { + bool first_access = !HasPromptedForAccessToAddressBook(pref_service); + + // +[ABAddressBook sharedAddressBook] throws an exception internally in + // circumstances that aren't clear. The exceptions are only observed in crash + // reports, so it is unknown whether they would be caught by AppKit and nil + // returned, or if they would take down the app. In either case, avoid + // crashing. http://crbug.com/129022 + ABAddressBook* addressBook = base::mac::RunBlockIgnoringExceptions( + ^{ return [ABAddressBook sharedAddressBook]; }); + UMA_HISTOGRAM_BOOLEAN("Autofill.AddressBookAvailable", addressBook != nil); + + if (first_access) { + UMA_HISTOGRAM_BOOLEAN("Autofill.AddressBookAvailableOnFirstAttempt", + addressBook != nil); + } + + pref_service->SetBoolean(prefs::kAutofillMacAddressBookQueried, true); + return addressBook; +} + // This implementation makes use of the Address Book API. Profiles are // generated that correspond to addresses in the "me" card that reside in the // user's Address Book. The caller passes a vector of profiles into the @@ -81,20 +116,12 @@ void AuxiliaryProfilesImpl::GetAddressBookMeCard(const std::string& app_locale, PrefService* pref_service) { profiles_.clear(); - // +[ABAddressBook sharedAddressBook] throws an exception internally in - // circumstances that aren't clear. The exceptions are only observed in crash - // reports, so it is unknown whether they would be caught by AppKit and nil - // returned, or if they would take down the app. In either case, avoid - // crashing. http://crbug.com/129022 - ABAddressBook* addressBook = base::mac::RunBlockIgnoringExceptions(^{ - return [ABAddressBook sharedAddressBook]; - }); - UMA_HISTOGRAM_BOOLEAN("Autofill.AddressBookAvailable", addressBook != nil); - if (!pref_service->GetBoolean(prefs::kAutofillAuxiliaryProfilesQueried)) { - pref_service->SetBoolean(prefs::kAutofillAuxiliaryProfilesQueried, true); - UMA_HISTOGRAM_BOOLEAN("Autofill.AddressBookAvailableOnFirstAttempt", - addressBook != nil); - } + // The user does not want Chrome to use the AddressBook to populate Autofill + // entries. + if (!ShouldUseAddressBook(pref_service)) + return; + + ABAddressBook* addressBook = GetAddressBook(pref_service); ABPerson* me = [addressBook me]; if (!me) @@ -281,4 +308,39 @@ void PersonalDataManager::LoadAuxiliaryProfiles() const { impl.GetAddressBookMeCard(app_locale_, pref_service_); } +bool PersonalDataManager::AccessAddressBook() { + // The user is attempting to give Chrome access to the user's Address Book. + // This implicitly acknowledges that the user wants to use auxiliary + // profiles. + pref_service_->SetBoolean(prefs::kAutofillUseMacAddressBook, true); + + // Request permissions. + GetAddressBook(pref_service_); + return true; +} + +bool PersonalDataManager::ShouldShowAccessAddressBookSuggestion( + AutofillType type) { + if (HasPromptedForAccessToAddressBook(pref_service_)) + return false; + + switch (type.group()) { + case ADDRESS_BILLING: + case ADDRESS_HOME: + case EMAIL: + case NAME: + case NAME_BILLING: + case PHONE_BILLING: + case PHONE_HOME: + return true; + case NO_GROUP: + case COMPANY: + case CREDIT_CARD: + case PASSWORD_FIELD: + return false; + } + + return false; +} + } // namespace autofill diff --git a/components/autofill/core/browser/popup_item_ids.h b/components/autofill/core/browser/popup_item_ids.h index 628e000..7a8e843 100644 --- a/components/autofill/core/browser/popup_item_ids.h +++ b/components/autofill/core/browser/popup_item_ids.h @@ -16,7 +16,8 @@ enum PopupItemId { POPUP_ITEM_ID_SEPARATOR = -3, POPUP_ITEM_ID_CLEAR_FORM = -4, POPUP_ITEM_ID_AUTOFILL_OPTIONS = -5, - POPUP_ITEM_ID_DATALIST_ENTRY = -6 + POPUP_ITEM_ID_DATALIST_ENTRY = -6, + POPUP_ITEM_ID_MAC_ACCESS_CONTACTS = -7, }; } // namespace autofill diff --git a/components/autofill/core/browser/test_autofill_driver.cc b/components/autofill/core/browser/test_autofill_driver.cc index ad796c0..f67c1b1 100644 --- a/components/autofill/core/browser/test_autofill_driver.cc +++ b/components/autofill/core/browser/test_autofill_driver.cc @@ -39,6 +39,8 @@ void TestAutofillDriver::SendFormDataToRenderer(int query_id, const FormData& form_data) { } +void TestAutofillDriver::PingRenderer() {} + void TestAutofillDriver::SendAutofillTypePredictionsToRenderer( const std::vector<FormStructure*>& forms) { } diff --git a/components/autofill/core/browser/test_autofill_driver.h b/components/autofill/core/browser/test_autofill_driver.h index e515ca7..34a8552 100644 --- a/components/autofill/core/browser/test_autofill_driver.h +++ b/components/autofill/core/browser/test_autofill_driver.h @@ -32,6 +32,7 @@ class TestAutofillDriver : public AutofillDriver { virtual void SendFormDataToRenderer(int query_id, RendererFormDataAction action, const FormData& data) OVERRIDE; + virtual void PingRenderer() OVERRIDE; virtual void SendAutofillTypePredictionsToRenderer( const std::vector<FormStructure*>& forms) OVERRIDE; virtual void RendererShouldAcceptDataListSuggestion( diff --git a/components/autofill/core/common/autofill_pref_names.cc b/components/autofill/core/common/autofill_pref_names.cc index d7385bb3..f35cb77 100644 --- a/components/autofill/core/common/autofill_pref_names.cc +++ b/components/autofill/core/common/autofill_pref_names.cc @@ -7,25 +7,30 @@ namespace autofill { namespace prefs { -// Boolean that is true when auxiliary Autofill profiles are enabled. -// Currently applies to Address Book "me" card on Mac. False on Win and Linux. +// Boolean that is true when auxiliary Autofill profiles are enabled. This pref +// is only relevant to Android. const char kAutofillAuxiliaryProfilesEnabled[] = "autofill.auxiliary_profiles_enabled"; -// Boolean that is true when at least one read request for auxiliary Autofill -// profiles has been issued. Currently applies only to auxiliary profiles on -// Mac. -const char kAutofillAuxiliaryProfilesQueried[] = - "autofill.auxiliary_profiles_queried"; - // Boolean that is true if Autofill is enabled and allowed to save profile data. const char kAutofillEnabled[] = "autofill.enabled"; +// Boolean that is true when Chrome has attempted to access the user's Address +// Book on Mac. This preference is important since the first time Chrome +// attempts to access the user's Address Book, Cocoa presents a blocking dialog +// to the user. +const char kAutofillMacAddressBookQueried[] = + "autofill.auxiliary_profiles_queried"; + // Double that indicates negative (for not matched forms) upload rate. const char kAutofillNegativeUploadRate[] = "autofill.negative_upload_rate"; // Double that indicates positive (for matched forms) upload rate. const char kAutofillPositiveUploadRate[] = "autofill.positive_upload_rate"; +// Whether Autofill should try to use the Mac Address Book. If this value is +// true, then kAutofillMacAddressBookQueried is expected to also be true. +const char kAutofillUseMacAddressBook[] = "autofill.use_mac_address_book"; + } // namespace prefs } // namespace autofill diff --git a/components/autofill/core/common/autofill_pref_names.h b/components/autofill/core/common/autofill_pref_names.h index 50af375f..fb580a6 100644 --- a/components/autofill/core/common/autofill_pref_names.h +++ b/components/autofill/core/common/autofill_pref_names.h @@ -11,10 +11,11 @@ namespace prefs { // Alphabetical list of preference names specific to the Autofill // component. Keep alphabetized, and document each in the .cc file. extern const char kAutofillAuxiliaryProfilesEnabled[]; -extern const char kAutofillAuxiliaryProfilesQueried[]; extern const char kAutofillEnabled[]; +extern const char kAutofillMacAddressBookQueried[]; extern const char kAutofillNegativeUploadRate[]; extern const char kAutofillPositiveUploadRate[]; +extern const char kAutofillUseMacAddressBook[]; } // namespace prefs } // namespace autofill |