Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / components / autofill / core / browser / personal_data_manager_mac.mm
index 745a60e..52d8e5f 100644 (file)
 #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 "grit/component_strings.h"
+#include "components/autofill/core/common/form_data.h"
+#include "grit/components_strings.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 
 namespace autofill {
 namespace {
 
+// There is an uncommon sequence of events that causes the Address Book
+// permissions dialog to appear more than once for a given install of Chrome.
+//  1. Chrome has previously presented the Address Book permissions dialog.
+//  2. Chrome is launched.
+//  3. Chrome performs an auto-update, and changes its binary.
+//  4. Chrome attempts to access the Address Book for the first time since (2).
+// This sequence of events is rare because Chrome attempts to acess the Address
+// Book when the user focuses most form fields, so (4) generally occurs before
+// (3). For more details, see http://crbug.com/381763.
+//
+// When this sequence of events does occur, Chrome should not attempt to access
+// the Address Book unless the user explicitly asks Chrome to do so. The
+// jarring nature of the permissions dialog is worse than the potential benefit
+// of pulling information from the Address Book.
+//
+// Set to true after the Address Book is accessed for the first time.
+static bool g_accessed_address_book = false;
+
+// Set to true after the Chrome binary has been changed.
+static bool g_binary_changed = false;
+
 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);
+}
+
+// Records a UMA metric indicating whether an attempt to access the Address
+// Book was skipped because doing so would cause the Address Book permissions
+// prompt to incorrectly appear.
+void RecordAccessSkipped(bool skipped) {
+  UMA_HISTOGRAM_BOOLEAN("Autofill.AddressBook.AccessSkipped", skipped);
+}
+
+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);
+  }
+
+  g_accessed_address_book = true;
+  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
@@ -50,7 +114,8 @@ class AuxiliaryProfilesImpl {
 
   // Import the "me" card from the Mac Address Book and fill in |profiles_|.
   void GetAddressBookMeCard(const std::string& app_locale,
-                            PrefService* pref_service);
+                            PrefService* pref_service,
+                            bool record_metrics);
 
  private:
   void GetAddressBookNames(ABPerson* me,
@@ -78,24 +143,28 @@ class AuxiliaryProfilesImpl {
 // information and translates it to the internal list of |AutofillProfile| data
 // structures.
 void AuxiliaryProfilesImpl::GetAddressBookMeCard(const std::string& app_locale,
-                                                 PrefService* pref_service) {
+                                                 PrefService* pref_service,
+                                                 bool record_metrics) {
   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;
+
+  // See the comment at the definition of g_accessed_address_book for an
+  // explanation of this logic.
+  if (g_binary_changed && !g_accessed_address_book) {
+    if (record_metrics)
+      RecordAccessSkipped(true);
+    return;
   }
 
+  if (record_metrics)
+    RecordAccessSkipped(false);
+
+  ABAddressBook* addressBook = GetAddressBook(pref_service);
+
   ABPerson* me = [addressBook me];
   if (!me)
     return;
@@ -276,9 +345,49 @@ void AuxiliaryProfilesImpl::GetAddressBookPhoneNumbers(
 }  // namespace
 
 // Populate |auxiliary_profiles_| with the Address Book data.
-void PersonalDataManager::LoadAuxiliaryProfiles() const {
+void PersonalDataManager::LoadAuxiliaryProfiles(bool record_metrics) const {
   AuxiliaryProfilesImpl impl(&auxiliary_profiles_);
-  impl.GetAddressBookMeCard(app_locale_, pref_service_);
+  impl.GetAddressBookMeCard(app_locale_, pref_service_, record_metrics);
+}
+
+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:
+    case TRANSACTION:
+      return false;
+  }
+
+  return false;
+}
+
+void PersonalDataManager::BinaryChanging() {
+  g_binary_changed = true;
 }
 
 }  // namespace autofill