// 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/password_manager/core/browser/password_form_metrics_recorder.h"

#include <algorithm>

#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/user_metrics.h"
#include "base/numerics/safe_conversions.h"
#include "components/autofill/core/common/form_data.h"
#include "components/autofill/core/common/password_generation_util.h"
#include "components/password_manager/core/browser/form_fetcher.h"
#include "components/password_manager/core/browser/password_bubble_experiment.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/password_manager/core/browser/statistics_table.h"

using autofill::FieldPropertiesFlags;
using autofill::FormData;
using autofill::FormFieldData;
using autofill::PasswordForm;

namespace password_manager {

namespace {

PasswordFormMetricsRecorder::BubbleDismissalReason GetBubbleDismissalReason(
    metrics_util::UIDismissalReason ui_dismissal_reason) {
  using BubbleDismissalReason =
      PasswordFormMetricsRecorder::BubbleDismissalReason;
  switch (ui_dismissal_reason) {
    // Accepted by user.
    case metrics_util::CLICKED_SAVE:
      return BubbleDismissalReason::kAccepted;

    // Declined by user.
    case metrics_util::CLICKED_CANCEL:
    case metrics_util::CLICKED_NEVER:
      return BubbleDismissalReason::kDeclined;

    // Ignored by user.
    case metrics_util::NO_DIRECT_INTERACTION:
      return BubbleDismissalReason::kIgnored;

    // Ignore these for metrics collection:
    case metrics_util::CLICKED_MANAGE:
    case metrics_util::CLICKED_PASSWORDS_DASHBOARD:
    case metrics_util::AUTO_SIGNIN_TOAST_TIMEOUT:
      break;

    // These should not reach here:
    case metrics_util::CLICKED_DONE_OBSOLETE:
    case metrics_util::CLICKED_OK_OBSOLETE:
    case metrics_util::CLICKED_UNBLACKLIST_OBSOLETE:
    case metrics_util::CLICKED_CREDENTIAL_OBSOLETE:
    case metrics_util::AUTO_SIGNIN_TOAST_CLICKED_OBSOLETE:
    case metrics_util::CLICKED_BRAND_NAME_OBSOLETE:
    case metrics_util::NUM_UI_RESPONSES:
      NOTREACHED();
      break;
  }
  return BubbleDismissalReason::kUnknown;
}

bool HasGeneratedPassword(
    base::Optional<PasswordFormMetricsRecorder::GeneratedPasswordStatus>
        status) {
  return status.has_value() &&
         (status == PasswordFormMetricsRecorder::GeneratedPasswordStatus::
                        kPasswordAccepted ||
          status == PasswordFormMetricsRecorder::GeneratedPasswordStatus::
                        kPasswordEdited);
}

// Contains information whether saved username/password were filled or typed.
struct UsernamePasswordsState {
  bool saved_password_typed = false;
  bool saved_username_typed = false;
  bool password_manually_filled = false;
  bool username_manually_filled = false;
  bool password_automatically_filled = false;
  bool username_automatically_filled = false;
  bool unknown_password_typed = false;

  bool IsPasswordFilled() {
    return password_automatically_filled || password_manually_filled;
  }
};

// Calculates whether saved usernames/passwords were filled or typed in
// |submitted_form|.
UsernamePasswordsState CalculateUsernamePasswordsState(
    const FormData& submitted_form,
    const std::set<base::string16>& saved_usernames,
    const std::set<base::string16>& saved_passwords) {
  UsernamePasswordsState result;

  for (const FormFieldData& field : submitted_form.fields) {
    const base::string16& value =
        field.typed_value.empty() ? field.value : field.typed_value;

    bool user_typed = field.properties_mask & FieldPropertiesFlags::USER_TYPED;
    bool manually_filled = field.properties_mask &
                           FieldPropertiesFlags::AUTOFILLED_ON_USER_TRIGGER;
    bool automatically_filled =
        field.properties_mask & FieldPropertiesFlags::AUTOFILLED_ON_PAGELOAD;

    if (saved_usernames.count(value)) {
      result.saved_username_typed |= user_typed;
      result.username_manually_filled |= manually_filled;
      result.username_automatically_filled |= automatically_filled;
    } else if (saved_passwords.count(value)) {
      result.saved_password_typed |= user_typed;
      result.password_manually_filled |= manually_filled;
      result.password_automatically_filled |= automatically_filled;
    } else if (user_typed && field.form_control_type == "password") {
      result.unknown_password_typed = true;
    }
  }

  return result;
}

// Returns whether any value of |submitted_form| is listed in the
// |interactions_stats| has having been prompted to save as a credential and
// being ignored too often.
bool BlacklistedBySmartBubble(
    const FormData& submitted_form,
    const std::vector<InteractionsStats>& interactions_stats) {
  const int show_threshold =
      password_bubble_experiment::GetSmartBubbleDismissalThreshold();
  for (const FormFieldData& field : submitted_form.fields) {
    const base::string16& value =
        field.typed_value.empty() ? field.value : field.typed_value;
    for (const InteractionsStats& stat : interactions_stats) {
      if (stat.username_value == value &&
          stat.dismissal_count >= show_threshold)
        return true;
    }
  }
  return false;
}

}  // namespace

PasswordFormMetricsRecorder::PasswordFormMetricsRecorder(
    bool is_main_frame_secure,
    ukm::SourceId source_id)
    : is_main_frame_secure_(is_main_frame_secure),
      source_id_(source_id),
      ukm_entry_builder_(source_id) {}

PasswordFormMetricsRecorder::~PasswordFormMetricsRecorder() {
  UMA_HISTOGRAM_ENUMERATION("PasswordManager.ActionsTakenV3", GetActionsTaken(),
                            kMaxNumActionsTaken);
  ukm_entry_builder_.SetUser_ActionSimplified(
      static_cast<int64_t>(user_action_));

  // Use the visible main frame URL at the time the PasswordFormManager
  // is created, in case a navigation has already started and the
  // visible URL has changed.
  if (!is_main_frame_secure_) {
    UMA_HISTOGRAM_ENUMERATION("PasswordManager.ActionsTakenOnNonSecureForm",
                              GetActionsTaken(), kMaxNumActionsTaken);
  }

  if (submit_result_ == kSubmitResultNotSubmitted) {
    if (HasGeneratedPassword(generated_password_status_)) {
      metrics_util::LogPasswordGenerationSubmissionEvent(
          metrics_util::PASSWORD_NOT_SUBMITTED);
    } else if (generation_available_) {
      metrics_util::LogPasswordGenerationAvailableSubmissionEvent(
          metrics_util::PASSWORD_NOT_SUBMITTED);
    }
    ukm_entry_builder_.SetSubmission_Observed(0 /*false*/);
  }

  if (submitted_form_type_ != kSubmittedFormTypeUnspecified) {
    UMA_HISTOGRAM_ENUMERATION("PasswordManager.SubmittedFormType",
                              submitted_form_type_, kSubmittedFormTypeMax);
    if (!is_main_frame_secure_) {
      UMA_HISTOGRAM_ENUMERATION("PasswordManager.SubmittedNonSecureFormType",
                                submitted_form_type_, kSubmittedFormTypeMax);
    }

    ukm_entry_builder_.SetSubmission_SubmittedFormType(submitted_form_type_);
  }

  ukm_entry_builder_.SetUpdating_Prompt_Shown(update_prompt_shown_);
  ukm_entry_builder_.SetSaving_Prompt_Shown(save_prompt_shown_);

  for (const auto& action : detailed_user_actions_counts_) {
    switch (action.first) {
      case DetailedUserAction::kEditedUsernameInBubble:
        ukm_entry_builder_.SetUser_Action_EditedUsernameInBubble(action.second);
        break;
      case DetailedUserAction::kSelectedDifferentPasswordInBubble:
        ukm_entry_builder_.SetUser_Action_SelectedDifferentPasswordInBubble(
            action.second);
        break;
      case DetailedUserAction::kTriggeredManualFallbackForSaving:
        ukm_entry_builder_.SetUser_Action_TriggeredManualFallbackForSaving(
            action.second);
        break;
      case DetailedUserAction::kCorrectedUsernameInForm:
        ukm_entry_builder_.SetUser_Action_CorrectedUsernameInForm(
            action.second);
        break;
      case DetailedUserAction::kObsoleteTriggeredManualFallbackForUpdating:
        NOTREACHED();
        break;
    }
  }

  ukm_entry_builder_.SetGeneration_GeneratedPassword(
      HasGeneratedPassword(generated_password_status_));
  if (HasGeneratedPassword(generated_password_status_)) {
    ukm_entry_builder_.SetGeneration_GeneratedPasswordModified(
        generated_password_status_ !=
        GeneratedPasswordStatus::kPasswordAccepted);
  }
  if (generated_password_status_.has_value()) {
    // static cast to bypass a compilation error.
    UMA_HISTOGRAM_ENUMERATION("PasswordGeneration.UserDecision",
                              static_cast<GeneratedPasswordStatus>(
                                  generated_password_status_.value()));
  }

  if (password_generation_popup_shown_ !=
      PasswordGenerationPopupShown::kNotShown) {
    ukm_entry_builder_.SetGeneration_PopupShown(
        static_cast<int64_t>(password_generation_popup_shown_));
  }
  if (spec_priority_of_generated_password_) {
    ukm_entry_builder_.SetGeneration_SpecPriority(
        spec_priority_of_generated_password_.value());
  }

  if (showed_manual_fallback_for_saving_) {
    ukm_entry_builder_.SetSaving_ShowedManualFallbackForSaving(
        showed_manual_fallback_for_saving_.value());
  }

  if (form_changes_bitmask_) {
    UMA_HISTOGRAM_ENUMERATION("PasswordManager.DynamicFormChanges",
                              *form_changes_bitmask_,
                              static_cast<uint32_t>(kMaxFormDifferencesValue));
  }

  if (submit_result_ == kSubmitResultPassed && filling_assistance_) {
    FillingAssistance filling_assistance = *filling_assistance_;
    UMA_HISTOGRAM_ENUMERATION("PasswordManager.FillingAssistance",
                              filling_assistance);
    ukm_entry_builder_.SetManagerFill_Assistance(
        static_cast<int64_t>(filling_assistance));

    if (is_main_frame_secure_) {
      UMA_HISTOGRAM_ENUMERATION(
          "PasswordManager.FillingAssistance.SecureOrigin", filling_assistance);
    } else {
      UMA_HISTOGRAM_ENUMERATION(
          "PasswordManager.FillingAssistance.InsecureOrigin",
          filling_assistance);
    }
  }

  ukm_entry_builder_.Record(ukm::UkmRecorder::Get());
}

void PasswordFormMetricsRecorder::MarkGenerationAvailable() {
  generation_available_ = true;
}

void PasswordFormMetricsRecorder::SetGeneratedPasswordStatus(
    GeneratedPasswordStatus status) {
  generated_password_status_ = status;
}

void PasswordFormMetricsRecorder::ReportSpecPriorityForGeneratedPassword(
    uint32_t spec_priority) {
  spec_priority_of_generated_password_ = spec_priority;
}

void PasswordFormMetricsRecorder::SetManagerAction(
    ManagerAction manager_action) {
  manager_action_ = manager_action;
}

void PasswordFormMetricsRecorder::CalculateUserAction(
    const std::map<base::string16, const autofill::PasswordForm*>& best_matches,
    const autofill::PasswordForm& submitted_form) {
  const base::string16& submitted_password =
      !submitted_form.new_password_value.empty()
          ? submitted_form.new_password_value
          : submitted_form.password_value;

  if (submitted_form.username_value.empty()) {
    // In case the submitted form does not have a username field we do not
    // autofill. Thus the user either explicitly chose this credential from the
    // dropdown, or created a new password.
    for (const auto& match : best_matches) {
      if (match.second->password_value == submitted_password) {
        user_action_ = UserAction::kChoose;
        return;
      }
    }

    user_action_ = UserAction::kOverridePassword;
    return;
  }

  // In case the submitted form has a username value, check if there is an
  // existing match with the same username. If not, the user created a new
  // credential.
  auto found = best_matches.find(submitted_form.username_value);
  if (found == best_matches.end()) {
    user_action_ = UserAction::kOverrideUsernameAndPassword;
    return;
  }

  // Otherwise check if the user changed the password.
  const autofill::PasswordForm* existing_match = found->second;
  if (existing_match->password_value != submitted_password) {
    user_action_ = UserAction::kOverridePassword;
    return;
  }

  // If the existing match is a PSL match, the user purposefully chose it, since
  // PSL credentials are not autofilled.
  if (existing_match->is_public_suffix_match) {
    user_action_ = UserAction::kChoosePslMatch;
    return;
  }

  // Lastly, in case the existing match is not a preferred match, or the form
  // was not filled on page load, the user purposefully chose a credential.
  // Otherwise the user either did not do anything, or re-selected the default
  // option.
  if (!existing_match->preferred ||
      manager_action_ != kManagerActionAutofilled) {
    user_action_ = UserAction::kChoose;
    return;
  }

  user_action_ = UserAction::kNone;
}

void PasswordFormMetricsRecorder::SetUserActionForTesting(
    UserAction user_action) {
  user_action_ = user_action;
}

UserAction PasswordFormMetricsRecorder::GetUserAction() const {
  return user_action_;
}

void PasswordFormMetricsRecorder::LogSubmitPassed() {
  if (submit_result_ != kSubmitResultFailed) {
    if (HasGeneratedPassword(generated_password_status_)) {
      metrics_util::LogPasswordGenerationSubmissionEvent(
          metrics_util::PASSWORD_SUBMITTED);
    } else if (generation_available_) {
      metrics_util::LogPasswordGenerationAvailableSubmissionEvent(
          metrics_util::PASSWORD_SUBMITTED);
    }
  }
  base::RecordAction(base::UserMetricsAction("PasswordManager_LoginPassed"));
  ukm_entry_builder_.SetSubmission_Observed(1 /*true*/);
  ukm_entry_builder_.SetSubmission_SubmissionResult(kSubmitResultPassed);
  submit_result_ = kSubmitResultPassed;
}

void PasswordFormMetricsRecorder::LogSubmitFailed() {
  if (HasGeneratedPassword(generated_password_status_)) {
    metrics_util::LogPasswordGenerationSubmissionEvent(
        metrics_util::GENERATED_PASSWORD_FORCE_SAVED);
  } else if (generation_available_) {
    metrics_util::LogPasswordGenerationAvailableSubmissionEvent(
        metrics_util::PASSWORD_SUBMISSION_FAILED);
  }
  base::RecordAction(base::UserMetricsAction("PasswordManager_LoginFailed"));
  ukm_entry_builder_.SetSubmission_Observed(1 /*true*/);
  ukm_entry_builder_.SetSubmission_SubmissionResult(kSubmitResultFailed);
  submit_result_ = kSubmitResultFailed;
}

void PasswordFormMetricsRecorder::SetPasswordGenerationPopupShown(
    bool generation_popup_was_shown,
    bool is_manual_generation) {
  password_generation_popup_shown_ =
      generation_popup_was_shown
          ? (is_manual_generation
                 ? PasswordGenerationPopupShown::kShownManually
                 : PasswordGenerationPopupShown::kShownAutomatically)
          : PasswordGenerationPopupShown::kNotShown;
}

void PasswordFormMetricsRecorder::SetSubmittedFormType(
    SubmittedFormType form_type) {
  submitted_form_type_ = form_type;
}

void PasswordFormMetricsRecorder::SetSubmissionIndicatorEvent(
    autofill::SubmissionIndicatorEvent event) {
  ukm_entry_builder_.SetSubmission_Indicator(static_cast<int>(event));
}

int PasswordFormMetricsRecorder::GetActionsTakenNew() const {
  // Merge kManagerActionNone and kManagerActionBlacklisted_Obsolete. This
  // lowers the number of histogram buckets used by 33%.
  ManagerActionNew manager_action_new =
      (manager_action_ == kManagerActionAutofilled)
          ? kManagerActionNewAutofilled
          : kManagerActionNewNone;

  return static_cast<int>(user_action_) +
         static_cast<int>(UserAction::kMax) *
             (manager_action_new + kManagerActionNewMax * submit_result_);
}

void PasswordFormMetricsRecorder::RecordDetailedUserAction(
    PasswordFormMetricsRecorder::DetailedUserAction action) {
  detailed_user_actions_counts_[action]++;
}

// static
int64_t PasswordFormMetricsRecorder::HashFormSignature(
    autofill::FormSignature form_signature) {
  // Note that this is an intentionally small hash domain for privacy reasons.
  return static_cast<uint64_t>(form_signature) % 1021;
}

void PasswordFormMetricsRecorder::RecordFormSignature(
    autofill::FormSignature form_signature) {
  ukm_entry_builder_.SetContext_FormSignature(
      HashFormSignature(form_signature));
}

void PasswordFormMetricsRecorder::RecordParsingsComparisonResult(
    ParsingComparisonResult comparison_result) {
  ukm_entry_builder_.SetParsingComparison(
      static_cast<uint64_t>(comparison_result));
}

void PasswordFormMetricsRecorder::RecordParsingOnSavingDifference(
    uint64_t comparison_result) {
  ukm_entry_builder_.SetParsingOnSavingDifference(comparison_result);
}

void PasswordFormMetricsRecorder::RecordReadonlyWhenFilling(uint64_t value) {
  ukm_entry_builder_.SetReadonlyWhenFilling(value);
}

void PasswordFormMetricsRecorder::RecordReadonlyWhenSaving(uint64_t value) {
  ukm_entry_builder_.SetReadonlyWhenSaving(value);
}

void PasswordFormMetricsRecorder::RecordShowManualFallbackForSaving(
    bool has_generated_password,
    bool is_update) {
  showed_manual_fallback_for_saving_ =
      1 + (has_generated_password ? 2 : 0) + (is_update ? 4 : 0);
}

void PasswordFormMetricsRecorder::RecordFormChangeBitmask(uint32_t bitmask) {
  if (!form_changes_bitmask_)
    form_changes_bitmask_ = bitmask;
  else
    *form_changes_bitmask_ |= bitmask;
}

void PasswordFormMetricsRecorder::RecordFirstFillingResult(int32_t result) {
  if (recorded_first_filling_result_)
    return;
  ukm_entry_builder_.SetFill_FirstFillingResultInRenderer(result);
  recorded_first_filling_result_ = true;
}

void PasswordFormMetricsRecorder::RecordFirstWaitForUsernameReason(
    WaitForUsernameReason reason) {
  if (recorded_wait_for_username_reason_)
    return;
  UMA_HISTOGRAM_ENUMERATION("PasswordManager.FirstWaitForUsernameReason",
                            reason);
  ukm_entry_builder_.SetFill_FirstWaitForUsernameReason(
      static_cast<int64_t>(reason));
  recorded_wait_for_username_reason_ = true;
}

void PasswordFormMetricsRecorder::CalculateFillingAssistanceMetric(
    const FormData& submitted_form,
    const std::set<base::string16>& saved_usernames,
    const std::set<base::string16>& saved_passwords,
    const std::vector<InteractionsStats>& interactions_stats) {
  // If the user asked to never save credentials on a domain, an entry with
  // empty password exists for that domain.
  bool blacklisted =
      saved_passwords.find(base::string16()) != saved_passwords.end();
  if (blacklisted && saved_passwords.size() == 1u) {
    // Note that we miss a nuance here:
    //
    // It is possible that the user logs in to a.example.com but b.example.com
    // is blacklisted. We would still report kNoStoredCredentialsAndBlacklisted
    // even though the user has not blacklisted a.example.com and is asked
    // whether they want to save the credentials.
    filling_assistance_ = FillingAssistance::kNoSavedCredentialsAndBlacklisted;
    return;
  }

  if (saved_passwords.empty()) {
    filling_assistance_ =
        BlacklistedBySmartBubble(submitted_form, interactions_stats)
            ? FillingAssistance::kNoSavedCredentialsAndBlacklistedBySmartBubble
            : FillingAssistance::kNoSavedCredentials;
    return;
  }

  // Saved credentials are assumed to be correct as they match stored
  // credentials in subsequent calculations.

  UsernamePasswordsState username_password_state =
      CalculateUsernamePasswordsState(submitted_form, saved_usernames,
                                      saved_passwords);

  // Consider cases when the user typed known or unknown credentials.
  if (username_password_state.saved_password_typed) {
    filling_assistance_ = FillingAssistance::kKnownPasswordTyped;
    return;
  }

  if (!username_password_state.IsPasswordFilled()) {
    filling_assistance_ =
        username_password_state.unknown_password_typed
            ? FillingAssistance::kNewPasswordTypedWhileCredentialsExisted
            : FillingAssistance::kNoUserInputNoFillingInPasswordFields;
    return;
  }

  if (username_password_state.saved_username_typed) {
    filling_assistance_ = FillingAssistance::kUsernameTypedPasswordFilled;
    return;
  }

  // Cases related to user typing are already considered and excluded. Only
  // filling related cases are left.
  if (username_password_state.username_manually_filled ||
      username_password_state.password_manually_filled) {
    filling_assistance_ = FillingAssistance::kManual;
    return;
  }

  if (username_password_state.password_automatically_filled) {
    filling_assistance_ = FillingAssistance::kAutomatic;
    return;
  }

  // If execution gets here, we have a bug in our state machine.
  NOTREACHED();
}

int PasswordFormMetricsRecorder::GetActionsTaken() const {
  return static_cast<int>(user_action_) +
         static_cast<int>(UserAction::kMax) *
             (manager_action_ + kManagerActionMax * submit_result_);
}

void PasswordFormMetricsRecorder::RecordHistogramsOnSuppressedAccounts(
    bool observed_form_origin_has_cryptographic_scheme,
    const FormFetcher& form_fetcher,
    const PasswordForm& pending_credentials) {
  UMA_HISTOGRAM_BOOLEAN("PasswordManager.QueryingSuppressedAccountsFinished",
                        form_fetcher.DidCompleteQueryingSuppressedForms());

  if (!form_fetcher.DidCompleteQueryingSuppressedForms())
    return;

  SuppressedAccountExistence best_match = kSuppressedAccountNone;

  if (!observed_form_origin_has_cryptographic_scheme) {
    best_match = GetBestMatchingSuppressedAccount(
        form_fetcher.GetSuppressedHTTPSForms(), PasswordForm::TYPE_GENERATED,
        pending_credentials);
    UMA_HISTOGRAM_ENUMERATION(
        "PasswordManager.SuppressedAccount.Generated.HTTPSNotHTTP",
        GetHistogramSampleForSuppressedAccounts(best_match),
        kMaxSuppressedAccountStats);
    ukm_entry_builder_.SetSuppressedAccount_Generated_HTTPSNotHTTP(best_match);

    best_match = GetBestMatchingSuppressedAccount(
        form_fetcher.GetSuppressedHTTPSForms(), PasswordForm::TYPE_MANUAL,
        pending_credentials);
    UMA_HISTOGRAM_ENUMERATION(
        "PasswordManager.SuppressedAccount.Manual.HTTPSNotHTTP",
        GetHistogramSampleForSuppressedAccounts(best_match),
        kMaxSuppressedAccountStats);
    ukm_entry_builder_.SetSuppressedAccount_Manual_HTTPSNotHTTP(best_match);
  }

  best_match = GetBestMatchingSuppressedAccount(
      form_fetcher.GetSuppressedPSLMatchingForms(),
      PasswordForm::TYPE_GENERATED, pending_credentials);
  UMA_HISTOGRAM_ENUMERATION(
      "PasswordManager.SuppressedAccount.Generated.PSLMatching",
      GetHistogramSampleForSuppressedAccounts(best_match),
      kMaxSuppressedAccountStats);
  ukm_entry_builder_.SetSuppressedAccount_Generated_PSLMatching(best_match);
  best_match = GetBestMatchingSuppressedAccount(
      form_fetcher.GetSuppressedPSLMatchingForms(), PasswordForm::TYPE_MANUAL,
      pending_credentials);
  UMA_HISTOGRAM_ENUMERATION(
      "PasswordManager.SuppressedAccount.Manual.PSLMatching",
      GetHistogramSampleForSuppressedAccounts(best_match),
      kMaxSuppressedAccountStats);
  ukm_entry_builder_.SetSuppressedAccount_Manual_PSLMatching(best_match);

  best_match = GetBestMatchingSuppressedAccount(
      form_fetcher.GetSuppressedSameOrganizationNameForms(),
      PasswordForm::TYPE_GENERATED, pending_credentials);
  UMA_HISTOGRAM_ENUMERATION(
      "PasswordManager.SuppressedAccount.Generated.SameOrganizationName",
      GetHistogramSampleForSuppressedAccounts(best_match),
      kMaxSuppressedAccountStats);
  ukm_entry_builder_.SetSuppressedAccount_Generated_SameOrganizationName(
      best_match);
  best_match = GetBestMatchingSuppressedAccount(
      form_fetcher.GetSuppressedSameOrganizationNameForms(),
      PasswordForm::TYPE_MANUAL, pending_credentials);
  UMA_HISTOGRAM_ENUMERATION(
      "PasswordManager.SuppressedAccount.Manual.SameOrganizationName",
      GetHistogramSampleForSuppressedAccounts(best_match),
      kMaxSuppressedAccountStats);
  ukm_entry_builder_.SetSuppressedAccount_Manual_SameOrganizationName(
      best_match);

  if (current_bubble_ != CurrentBubbleOfInterest::kNone)
    RecordUIDismissalReason(metrics_util::NO_DIRECT_INTERACTION);
}

void PasswordFormMetricsRecorder::RecordPasswordBubbleShown(
    metrics_util::CredentialSourceType credential_source_type,
    metrics_util::UIDisplayDisposition display_disposition) {
  if (credential_source_type == metrics_util::CredentialSourceType::kUnknown)
    return;
  DCHECK_EQ(CurrentBubbleOfInterest::kNone, current_bubble_);
  BubbleTrigger automatic_trigger_type =
      credential_source_type ==
              metrics_util::CredentialSourceType::kPasswordManager
          ? BubbleTrigger::kPasswordManagerSuggestionAutomatic
          : BubbleTrigger::kCredentialManagementAPIAutomatic;
  BubbleTrigger manual_trigger_type =
      credential_source_type ==
              metrics_util::CredentialSourceType::kPasswordManager
          ? BubbleTrigger::kPasswordManagerSuggestionManual
          : BubbleTrigger::kCredentialManagementAPIManual;

  switch (display_disposition) {
    // New credential cases:
    case metrics_util::AUTOMATIC_WITH_PASSWORD_PENDING:
      current_bubble_ = CurrentBubbleOfInterest::kSaveBubble;
      save_prompt_shown_ = true;
      ukm_entry_builder_.SetSaving_Prompt_Trigger(
          static_cast<int64_t>(automatic_trigger_type));
      break;
    case metrics_util::MANUAL_WITH_PASSWORD_PENDING:
      current_bubble_ = CurrentBubbleOfInterest::kSaveBubble;
      save_prompt_shown_ = true;
      ukm_entry_builder_.SetSaving_Prompt_Trigger(
          static_cast<int64_t>(manual_trigger_type));
      break;

    // Update cases:
    case metrics_util::AUTOMATIC_WITH_PASSWORD_PENDING_UPDATE:
      current_bubble_ = CurrentBubbleOfInterest::kUpdateBubble;
      update_prompt_shown_ = true;
      ukm_entry_builder_.SetUpdating_Prompt_Trigger(
          static_cast<int64_t>(automatic_trigger_type));
      break;
    case metrics_util::MANUAL_WITH_PASSWORD_PENDING_UPDATE:
      current_bubble_ = CurrentBubbleOfInterest::kUpdateBubble;
      update_prompt_shown_ = true;
      ukm_entry_builder_.SetUpdating_Prompt_Trigger(
          static_cast<int64_t>(manual_trigger_type));
      break;

    // Other reasons to show a bubble:
    case metrics_util::MANUAL_MANAGE_PASSWORDS:
    case metrics_util::AUTOMATIC_GENERATED_PASSWORD_CONFIRMATION:
    case metrics_util::MANUAL_GENERATED_PASSWORD_CONFIRMATION:
    case metrics_util::AUTOMATIC_SIGNIN_TOAST:
      // Do nothing.
      return;

    // Obsolte display dispositions:
    case metrics_util::MANUAL_BLACKLISTED_OBSOLETE:
    case metrics_util::AUTOMATIC_CREDENTIAL_REQUEST_OBSOLETE:
    case metrics_util::NUM_DISPLAY_DISPOSITIONS:
      NOTREACHED();
      return;
  }
}

void PasswordFormMetricsRecorder::RecordUIDismissalReason(
    metrics_util::UIDismissalReason ui_dismissal_reason) {
  if (current_bubble_ != CurrentBubbleOfInterest::kUpdateBubble &&
      current_bubble_ != CurrentBubbleOfInterest::kSaveBubble)
    return;
  auto bubble_dismissal_reason = GetBubbleDismissalReason(ui_dismissal_reason);
  if (bubble_dismissal_reason != BubbleDismissalReason::kUnknown) {
    if (current_bubble_ == CurrentBubbleOfInterest::kUpdateBubble) {
      ukm_entry_builder_.SetUpdating_Prompt_Interaction(
          static_cast<int64_t>(bubble_dismissal_reason));
    } else {
      ukm_entry_builder_.SetSaving_Prompt_Interaction(
          static_cast<int64_t>(bubble_dismissal_reason));
    }
  }

  current_bubble_ = CurrentBubbleOfInterest::kNone;
}

void PasswordFormMetricsRecorder::RecordFillEvent(ManagerAutofillEvent event) {
  ukm_entry_builder_.SetManagerFill_Action(event);
}

PasswordFormMetricsRecorder::SuppressedAccountExistence
PasswordFormMetricsRecorder::GetBestMatchingSuppressedAccount(
    const std::vector<const PasswordForm*>& suppressed_forms,
    PasswordForm::Type manual_or_generated,
    const PasswordForm& pending_credentials) const {
  SuppressedAccountExistence best_matching_account = kSuppressedAccountNone;
  for (const PasswordForm* form : suppressed_forms) {
    if (form->type != manual_or_generated)
      continue;

    SuppressedAccountExistence current_account;
    if (pending_credentials.password_value.empty())
      current_account = kSuppressedAccountExists;
    else if (form->username_value != pending_credentials.username_value)
      current_account = kSuppressedAccountExistsDifferentUsername;
    else if (form->password_value != pending_credentials.password_value)
      current_account = kSuppressedAccountExistsSameUsername;
    else
      current_account = kSuppressedAccountExistsSameUsernameAndPassword;

    best_matching_account = std::max(best_matching_account, current_account);
  }
  return best_matching_account;
}

int PasswordFormMetricsRecorder::GetHistogramSampleForSuppressedAccounts(
    SuppressedAccountExistence best_matching_account) const {
  // Encoding: most significant digit is the |best_matching_account|.
  int mixed_base_encoding = 0;
  mixed_base_encoding += best_matching_account;
  mixed_base_encoding *= PasswordFormMetricsRecorder::kMaxNumActionsTakenNew;
  mixed_base_encoding += GetActionsTakenNew();
  DCHECK_LT(mixed_base_encoding, kMaxSuppressedAccountStats);
  return mixed_base_encoding;
}

}  // namespace password_manager
