text: // Copyright 2018 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/votes_uploader.h"
  
  #include <ctype.h>
  #include <algorithm>
  #include <iostream>
  #include <utility>
  
  #include "base/check_op.h"
  #include "base/hash/hash.h"
  #include "base/metrics/histogram_macros.h"
  #include "base/rand_util.h"
  #include "base/ranges/algorithm.h"
  #include "base/strings/utf_string_conversions.h"
  #include "components/autofill/core/browser/autofill_download_manager.h"
  #include "components/autofill/core/browser/autofill_field.h"
  #include "components/autofill/core/browser/form_structure.h"
  #include "components/autofill/core/browser/randomized_encoder.h"
  #include "components/autofill/core/common/form_data.h"
  #include "components/autofill/core/common/form_field_data.h"
  #include "components/autofill/core/common/signatures.h"
  #include "components/password_manager/core/browser/browser_save_password_progress_logger.h"
  #include "components/password_manager/core/browser/field_info_manager.h"
  #include "components/password_manager/core/browser/password_manager_client.h"
  #include "components/password_manager/core/browser/password_manager_util.h"
  #include "third_party/abseil-cpp/absl/types/optional.h"
  
  using autofill::AutofillDownloadManager;
  using autofill::AutofillField;
  using autofill::AutofillUploadContents;
  using autofill::FieldSignature;
  using autofill::FormData;
  using autofill::FormFieldData;
  using autofill::FormStructure;
  using autofill::RandomizedEncoder;
  using autofill::ServerFieldType;
  using autofill::ServerFieldTypeSet;
  using password_manager_util::FindFormByUsername;
  
  using Logger = autofill::SavePasswordProgressLogger;
  using StringID = autofill::SavePasswordProgressLogger::StringID;
  
  namespace password_manager {
  
  namespace {
  // Number of distinct low-entropy hash values.
  constexpr uint32_t kNumberOfLowEntropyHashValues = 64;
  
  // Contains all special symbols considered for password-generation.
  constexpr char kSpecialSymbols[] = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
  
  // Sets autofill types of password and new password fields in |field_types|.
  // |password_type| (the autofill type of new password field) should be equal to
  // NEW_PASSWORD, PROBABLY_NEW_PASSWORD or NOT_NEW_PASSWORD. These values
  // correspond to cases when the user confirmed password update, did nothing or
  // declined to update password respectively.
  void SetFieldLabelsOnUpdate(const ServerFieldType password_type,
                              const PasswordForm& submitted_form,
                              FieldTypeMap* field_types) {
    DCHECK(password_type == autofill::NEW_PASSWORD ||
           password_type == autofill::PROBABLY_NEW_PASSWORD ||
           password_type == autofill::NOT_NEW_PASSWORD)
        << password_type;
    if (submitted_form.new_password_element.empty())
      return;
  
    (*field_types)[submitted_form.password_element] = autofill::PASSWORD;
    (*field_types)[submitted_form.new_password_element] = password_type;
  }
  
  // Sets the autofill type of the password field stored in |submitted_form| to
  // |password_type| in |field_types| map.
  void SetFieldLabelsOnSave(const ServerFieldType password_type,
                            const PasswordForm& form,
                            FieldTypeMap* field_types) {
    DCHECK(password_type == autofill::PASSWORD ||
           password_type == autofill::ACCOUNT_CREATION_PASSWORD ||
           password_type == autofill::NOT_ACCOUNT_CREATION_PASSWORD)
        << password_type;
  
    if (!form.new_password_element.empty())
      (*field_types)[form.new_password_element] = password_type;
    else if (!form.password_element.empty())
      (*field_types)[form.password_element] = password_type;
  }
  
  // Label username and password fields with autofill types in |form_structure|
  // based on |field_types|, and vote types based on |vote_types|. The function
  // also adds the types to |available_field_types|. For fields of |USERNAME|
  // type, a vote type must exist.
  void LabelFields(const FieldTypeMap& field_types,
                   const VoteTypeMap& vote_types,
                   FormStructure* form_structure,
                   ServerFieldTypeSet* available_field_types) {
    for (size_t i = 0; i < form_structure->field_count(); ++i) {
      AutofillField* field = form_structure->field(i);
  
      ServerFieldType type = autofill::UNKNOWN_TYPE;
      if (!field->name.empty()) {
        auto iter = field_types.find(field->name);
        if (iter != field_types.end()) {
          type = iter->second;
          available_field_types->insert(type);
        }
  
        auto vote_type_iter = vote_types.find(field->name);
        if (vote_type_iter != vote_types.end()) {
          field->set_vote_type(vote_type_iter->second);
        }
        DCHECK(type != autofill::USERNAME ||
               field->vote_type() !=
                   AutofillUploadContents::Field::NO_INFORMATION);
      }
      ServerFieldTypeSet types;
      types.insert(type);
      field->set_possible_types(types);
    }
  }
  
  // Returns true if |credentials| has the same password as an entry in |matches|
  // which doesn't have a username.
  bool IsAddingUsernameToExistingMatch(
      const PasswordForm& credentials,
      const std::vector<const PasswordForm*>& matches) {
    if (credentials.username_value.empty())
      return false;
    const PasswordForm* match = FindFormByUsername(matches, std::u16string());
    return match && !match->is_public_suffix_match &&
           match->password_value == credentials.password_value;
  }
  
  // Helper functions for character type classification. The built-in functions
  // depend on locale, platform and other stuff. To make the output more
  // predictable, the function are re-implemented here.
  bool IsNumeric(int c) {
    return '0' <= c && c <= '9';
  }
  bool IsLowercaseLetter(int c) {
    return 'a' <= c && c <= 'z';
  }
  bool IsUppercaseLetter(int c) {
    return 'A' <= c && c <= 'Z';
  }
  
  // Checks if a supplied character |c| is a special symbol.
  // Special symbols are defined by the string |kSpecialSymbols|.
  bool IsSpecialSymbol(int c) {
    return std::find(std::begin(kSpecialSymbols), std::end(kSpecialSymbols), c) !=
           std::end(kSpecialSymbols);
  }
  
  // Returns a uniformly distributed random symbol from the set of random symbols
  // defined by the string |kSpecialSymbols|.
  int GetRandomSpecialSymbol() {
    return kSpecialSymbols[base::RandGenerator(base::size(kSpecialSymbols))];
  }
  
  // Returns a random special symbol used in |password|.
  // It is expected that |password| contains at least one special symbol.
  int GetRandomSpecialSymbolFromPassword(const std::u16string& password) {
    std::vector<int> symbols;
    std::copy_if(password.begin(), password.end(), std::back_inserter(symbols),
                 &IsSpecialSymbol);
    DCHECK(!symbols.empty()) << "Password must contain at least one symbol.";
    return symbols[base::RandGenerator(symbols.size())];
  }
  
  size_t GetLowEntropyHashValue(const std::u16string& value) {
    return base::PersistentHash(base::UTF16ToUTF8(value)) %
           kNumberOfLowEntropyHashValues;
  }
  
  }  // namespace
  
  SingleUsernameVoteData::SingleUsernameVoteData(
      autofill::FieldRendererId renderer_id,
      const FormPredictions& form_predictions)
      : renderer_id(renderer_id), form_predictions(form_predictions) {}
  
  SingleUsernameVoteData::SingleUsernameVoteData(
      const SingleUsernameVoteData& other) = default;
  SingleUsernameVoteData& SingleUsernameVoteData::operator=(
      const SingleUsernameVoteData&) = default;
  SingleUsernameVoteData::SingleUsernameVoteData(SingleUsernameVoteData&& other) =
      default;
  SingleUsernameVoteData::~SingleUsernameVoteData() = default;
  
  VotesUploader::VotesUploader(PasswordManagerClient* client,
                               bool is_possible_change_password_form)
      : client_(client),
        is_possible_change_password_form_(is_possible_change_password_form) {}
  
  VotesUploader::VotesUploader(const VotesUploader& other) = default;
  VotesUploader::~VotesUploader() = default;
  
  void VotesUploader::SendVotesOnSave(
      const FormData& observed,
      const PasswordForm& submitted_form,
      const std::vector<const PasswordForm*>& best_matches,
      PasswordForm* pending_credentials) {
    if (pending_credentials->times_used == 1 ||
        IsAddingUsernameToExistingMatch(*pending_credentials, best_matches)) {
      UploadFirstLoginVotes(best_matches, *pending_credentials, submitted_form);
    }
  
    // Upload credentials the first time they are saved. This data is used
    // by password generation to help determine account creation sites.
    // Credentials that have been previously used (e.g., PSL matches) are checked
    // to see if they are valid account creation forms.
    if (pending_credentials->times_used == 0) {
      UploadPasswordVote(*pending_credentials, submitted_form, autofill::PASSWORD,
                         std::string());
      if (username_correction_vote_) {
        UploadPasswordVote(*username_correction_vote_, submitted_form,
                           autofill::USERNAME,
                           FormStructure(observed).FormSignatureAsStr());
      }
      MaybeSendSingleUsernameVote(true /* credentials_saved */);
    } else {
      SendVoteOnCredentialsReuse(observed, submitted_form, pending_credentials);
    }
  }
  
  void VotesUploader::SendVoteOnCredentialsReuse(
      const FormData& observed,
      const PasswordForm& submitted_form,
      PasswordForm* pending) {
    // Ignore |pending_structure| if its FormData has no fields. This is to
    // weed out those credentials that were saved before FormData was added
    // to PasswordForm. Even without this check, these FormStructure's won't
    // be uploaded, but it makes it hard to see if we are encountering
    // unexpected errors.
    if (pending->form_data.fields.empty())
      return;
  
    FormStructure pending_structure(pending->form_data);
    FormStructure observed_structure(observed);
  
    if (pending_structure.form_signature() !=
        observed_structure.form_signature()) {
      // Only upload if this is the first time the password has been used.
      // Otherwise the credentials have been used on the same field before so
      // they aren't from an account creation form.
      // Also bypass uploading if the username was edited. Offering generation
      // in cases where we currently save the wrong username isn't great.
      if (pending->times_used == 1) {
        if (UploadPasswordVote(*pending, submitted_form,
                               autofill::ACCOUNT_CREATION_PASSWORD,
                               observed_structure.FormSignatureAsStr())) {
          pending->generation_upload_status =
              PasswordForm::GenerationUploadStatus::kPositiveSignalSent;
        }
      }
    } else if (pending->generation_upload_status ==
               PasswordForm::GenerationUploadStatus::kPositiveSignalSent) {
      // A signal was sent that this was an account creation form, but the
      // credential is now being used on the same form again. This cancels out
      // the previous vote.
      if (UploadPasswordVote(*pending, submitted_form,
                             autofill::NOT_ACCOUNT_CREATION_PASSWORD,
                             std::string())) {
        pending->generation_upload_status =
            PasswordForm::GenerationUploadStatus::kNegativeSignalSent;
      }
    } else if (generation_popup_was_shown_) {
      // Even if there is no autofill vote to be sent, send the vote about the
      // usage of the generation popup.
      UploadPasswordVote(*pending, submitted_form, autofill::UNKNOWN_TYPE,
                         std::string());
    }
  }
  
  bool VotesUploader::UploadPasswordVote(
      const PasswordForm& form_to_upload,
      const PasswordForm& submitted_form,
      const ServerFieldType autofill_type,
      const std::string& login_form_signature) {
    // Check if there is any vote to be sent.
    bool has_autofill_vote = autofill_type != autofill::UNKNOWN_TYPE;
    bool has_password_generation_vote = generation_popup_was_shown_;
    if (!has_autofill_vote && !has_password_generation_vote)
      return false;
  
    if (form_to_upload.form_data.fields.empty()) {
      // List of fields may be empty in tests.
      return false;
    }
  
    AutofillDownloadManager* download_manager =
        client_->GetAutofillDownloadManager();
    if (!download_manager)
      return false;
  
    // If this is an update, a vote about the observed form is sent. If the user
    // re-uses credentials, a vote about the saved form is sent. If the user saves
    // credentials, the observed and pending forms are the same.
    FormStructure form_structure(form_to_upload.form_data);
    form_structure.set_submission_event(submitted_form.submission_event);
  
    ServerFieldTypeSet available_field_types;
    // A map from field names to field types.
    FieldTypeMap field_types;
    auto username_vote_type = AutofillUploadContents::Field::NO_INFORMATION;
    if (autofill_type != autofill::USERNAME) {
      if (has_autofill_vote) {
        bool is_update = autofill_type == autofill::NEW_PASSWORD ||
                         autofill_type == autofill::PROBABLY_NEW_PASSWORD ||
                         autofill_type == autofill::NOT_NEW_PASSWORD;
  
        if (is_update) {
          if (form_to_upload.new_password_element.empty())
            return false;
          SetFieldLabelsOnUpdate(autofill_type, form_to_upload, &field_types);
        } else {  // Saving.
          SetFieldLabelsOnSave(autofill_type, form_to_upload, &field_types);
        }
        if (autofill_type != autofill::ACCOUNT_CREATION_PASSWORD) {
          // If |autofill_type| == autofill::ACCOUNT_CREATION_PASSWORD, Chrome
          // will upload a vote for another form: the one that the credential was
          // saved on.
          field_types[submitted_form.confirmation_password_element] =
              autofill::CONFIRMATION_PASSWORD;
          form_structure.set_passwords_were_revealed(
              has_passwords_revealed_vote_);
        }
      }
      if (autofill_type != autofill::ACCOUNT_CREATION_PASSWORD) {
        if (generation_popup_was_shown_)
          AddGeneratedVote(&form_structure);
        if (username_change_state_ == UsernameChangeState::kChangedToKnownValue) {
          field_types[form_to_upload.username_element] = autofill::USERNAME;
          username_vote_type = AutofillUploadContents::Field::USERNAME_EDITED;
        }
      } else {  // User reuses credentials.
        // If the saved username value was used, then send a confirmation vote for
        // username.
        if (!submitted_form.username_value.empty()) {
          DCHECK(submitted_form.username_value == form_to_upload.username_value);
          field_types[form_to_upload.username_element] = autofill::USERNAME;
          username_vote_type = AutofillUploadContents::Field::CREDENTIALS_REUSED;
        }
      }
      if (autofill_type == autofill::PASSWORD) {
        // The password attributes should be uploaded only on the first save.
        DCHECK_EQ(form_to_upload.times_used, 0);
        GeneratePasswordAttributesVote(form_to_upload.password_value,
                                       &form_structure);
      }
    } else {  // User overwrites username.
      field_types[form_to_upload.username_element] = autofill::USERNAME;
      field_types[form_to_upload.password_element] =
          autofill::ACCOUNT_CREATION_PASSWORD;
      username_vote_type = AutofillUploadContents::Field::USERNAME_OVERWRITTEN;
    }
    LabelFields(field_types,
                {{form_to_upload.username_element, username_vote_type}},
                &form_structure, &available_field_types);
  
    // Force uploading as these events are relatively rare and we want to make
    // sure to receive them.
    form_structure.set_upload_required(UPLOAD_REQUIRED);
  
    if (password_manager_util::IsLoggingActive(client_)) {
      BrowserSavePasswordProgressLogger logger(client_->GetLogManager());
      logger.LogFormStructure(Logger::STRING_PASSWORD_FORM_VOTE, form_structure);
    }
  
    // Annotate the form with the source language of the page.
    form_structure.set_current_page_language(client_->GetPageLanguage());
  
    // Attach the Randomized Encoder.
    form_structure.set_randomized_encoder(
        RandomizedEncoder::Create(client_->GetPrefs()));
  
    // TODO(crbug.com/875768): Use VotesUploader::StartUploadRequest for avoiding
    // code duplication.
    bool success = download_manager->StartUploadRequest(
        form_structure, false /* was_autofilled */, available_field_types,
        login_form_signature, true /* observed_submission */,
        nullptr /* prefs */);
  
    UMA_HISTOGRAM_BOOLEAN("PasswordGeneration.UploadStarted", success);
    return success;
  }
  
  // TODO(crbug.com/840384): Share common code with UploadPasswordVote.
  void VotesUploader::UploadFirstLoginVotes(
      const std::vector<const PasswordForm*>& best_matches,
      const PasswordForm& pending_credentials,
      const PasswordForm& form_to_upload) {
    AutofillDownloadManager* download_manager =
        client_->GetAutofillDownloadManager();
    if (!download_manager)
      return;
  
    if (form_to_upload.form_data.fields.empty()) {
      // List of fields may be empty in tests.
      return;
    }
  
    FormStructure form_structure(form_to_upload.form_data);
    form_structure.set_submission_event(form_to_upload.submission_event);
  
    FieldTypeMap field_types = {
        {form_to_upload.username_element, autofill::USERNAME}};
    VoteTypeMap vote_types = {{form_to_upload.username_element,
                               AutofillUploadContents::Field::FIRST_USE}};
    if (!password_overridden_) {
      field_types[form_to_upload.password_element] = autofill::PASSWORD;
      vote_types[form_to_upload.password_element] =
          AutofillUploadContents::Field::FIRST_USE;
    }
  
    ServerFieldTypeSet available_field_types;
    LabelFields(field_types, vote_types, &form_structure, &available_field_types);
    SetKnownValueFlag(pending_credentials, best_matches, &form_structure);
  
    // Force uploading as these events are relatively rare and we want to make
    // sure to receive them.
    form_structure.set_upload_required(UPLOAD_REQUIRED);
  
    // Annotate the form with the source language of the page.
    form_structure.set_current_page_language(client_->GetPageLanguage());
  
    // Attach the Randomized Encoder.
    form_structure.set_randomized_encoder(
        RandomizedEncoder::Create(client_->GetPrefs()));
  
    SetInitialHashValueOfUsernameField(
        form_to_upload.username_element_renderer_id, &form_structure);
  
    if (password_manager_util::IsLoggingActive(client_)) {
      BrowserSavePasswordProgressLogger logger(client_->GetLogManager());
      logger.LogFormStructure(Logger::STRING_FIRSTUSE_FORM_VOTE, form_structure);
    }
  
    // TODO(crbug.com/875768): Use VotesUploader::StartUploadRequest for avoiding
    // code duplication.
    download_manager->StartUploadRequest(
        form_structure, false /* was_autofilled */, available_field_types,
        std::string(), true /* observed_submission */, nullptr /* prefs */);
  }
  
  void VotesUploader::SetInitialHashValueOfUsernameField(
      autofill::FieldRendererId username_element_renderer_id,
      FormStructure* form_structure) {
    auto it = initial_values_.find(username_element_renderer_id);
  
    if (it == initial_values_.end() || it->second.empty())
      return;
  
    for (const auto& field : *form_structure) {
      if (field && field->unique_renderer_id == username_element_renderer_id) {
        const std::u16string form_signature =
            base::UTF8ToUTF16(form_structure->FormSignatureAsStr());
        const std::u16string seeded_input = it->second.append(form_signature);
        field->set_initial_value_hash(GetLowEntropyHashValue(seeded_input));
        break;
      }
    }
  }
  
  void VotesUploader::MaybeSendSingleUsernameVote(bool credentials_saved) {
    if (!single_username_vote_data_)
      return;
  
    FieldInfoManager* field_info_manager = client_->GetFieldInfoManager();
    if (!field_info_manager)
      return;
  
    const FormPredictions& predictions =
        single_username_vote_data_->form_predictions;
    std::vector<FieldSignature> field_signatures;
    for (const auto& field : predictions.fields)
      field_signatures.push_back(field.signature);
  
    std::unique_ptr<FormStructure> form_to_upload =
        FormStructure::CreateForPasswordManagerUpload(predictions.form_signature,
                                                      field_signatures);
  
    // Label the username field with a SINGLE_USERNAME or NOT_USERNAME vote.
    // TODO(crbug.com/552420): Use LabelFields() when cl/1667453 is landed.
    ServerFieldTypeSet available_field_types;
    for (size_t i = 0; i < form_to_upload->field_count(); ++i) {
      AutofillField* field = form_to_upload->field(i);
  
      ServerFieldType type = autofill::UNKNOWN_TYPE;
      autofill::FieldRendererId field_renderer_id =
          predictions.fields[i].renderer_id;
      if (field_renderer_id == single_username_vote_data_->renderer_id) {
        if (field_info_manager->GetFieldType(predictions.form_signature,
                                             predictions.fields[i].signature) !=
            autofill::UNKNOWN_TYPE) {
          // The vote for this field has been already sent. Don't send again.
          return;
        }
        type = credentials_saved &&
                       username_change_state_ == UsernameChangeState::kUnchanged
                   ? autofill::SINGLE_USERNAME
                   : autofill::NOT_USERNAME;
        if (username_change_state_ == UsernameChangeState::kChangedToKnownValue)
          field->set_vote_type(AutofillUploadContents::Field::USERNAME_EDITED);
        available_field_types.insert(type);
        SaveFieldVote(form_to_upload->form_signature(),
                      field->GetFieldSignature(), type);
      }
      field->set_possible_types({type});
    }
  
    if (password_manager_util::IsLoggingActive(client_)) {
      BrowserSavePasswordProgressLogger logger(client_->GetLogManager());
      logger.LogFormStructure(Logger::STRING_USERNAME_FIRST_FLOW_VOTE,
                              *form_to_upload);
    }
  
    StartUploadRequest(std::move(form_to_upload), available_field_types);
  }
  
  void VotesUploader::AddGeneratedVote(FormStructure* form_structure) {
    DCHECK(form_structure);
    DCHECK(generation_popup_was_shown_);
  
    if (!generation_element_)
      return;
  
    AutofillUploadContents::Field::PasswordGenerationType type =
        AutofillUploadContents::Field::NO_GENERATION;
    if (has_generated_password_) {
      if (is_manual_generation_) {
        type = is_possible_change_password_form_
                   ? AutofillUploadContents::Field::
                         MANUALLY_TRIGGERED_GENERATION_ON_CHANGE_PASSWORD_FORM
                   : AutofillUploadContents::Field::
                         MANUALLY_TRIGGERED_GENERATION_ON_SIGN_UP_FORM;
      } else {
        type =
            is_possible_change_password_form_
                ? AutofillUploadContents::Field::
                      AUTOMATICALLY_TRIGGERED_GENERATION_ON_CHANGE_PASSWORD_FORM
                : AutofillUploadContents::Field::
                      AUTOMATICALLY_TRIGGERED_GENERATION_ON_SIGN_UP_FORM;
      }
    } else {
      type = AutofillUploadContents::Field::IGNORED_GENERATION_POPUP;
    }
  
    for (size_t i = 0; i < form_structure->field_count(); ++i) {
      AutofillField* field = form_structure->field(i);
      if (field->unique_renderer_id == generation_element_) {
        field->set_generation_type(type);
        if (has_generated_password_) {
          field->set_generated_password_changed(generated_password_changed_);
          UMA_HISTOGRAM_BOOLEAN("PasswordGeneration.GeneratedPasswordWasEdited",
                                generated_password_changed_);
        }
        break;
      }
    }
  }
  
  void VotesUploader::SetKnownValueFlag(
      const PasswordForm& pending_credentials,
      const std::vector<const PasswordForm*>& best_matches,
      FormStructure* form) {
    const std::u16string& known_username = pending_credentials.username_value;
    std::u16string known_password;
    if (password_overridden_) {
      // If we are updating a password, the known value should be the old
      // password, not the new one.
      const PasswordForm* match =
          FindFormByUsername(best_matches, known_username);
      if (!match) {
        // Username was not found, do nothing.
        return;
      }
      known_password = match->password_value;
    } else {
      known_password = pending_credentials.password_value;
    }
  
    // If we are updating a password, the known value is the old password, not
    // the new one.
    for (auto& field : *form) {
      if (field->value.empty())
        continue;
      if (known_username == field->value || known_password == field->value) {
        field->properties_mask |= autofill::FieldPropertiesFlags::kKnownValue;
      }
    }
  }
  
  bool VotesUploader::FindUsernameInOtherPossibleUsernames(
      const PasswordForm& match,
      const std::u16string& username) {
    DCHECK(!username_correction_vote_);
  
    for (const ValueElementPair& pair : match.all_possible_usernames) {
      if (pair.first == username) {
        username_correction_vote_ = match;
        username_correction_vote_->username_element = pair.second;
        return true;
      }
    }
    return false;
  }
  
  bool VotesUploader::FindCorrectedUsernameElement(
      const std::vector<const PasswordForm*>& matches,
      const std::u16string& username,
      const std::u16string& password) {
    if (username.empty())
      return false;
    for (const PasswordForm* match : matches) {
      if ((match->password_value == password) &&
          FindUsernameInOtherPossibleUsernames(*match, username)) {
        return true;
      }
    }
    return false;
  }
  
  void VotesUploader::GeneratePasswordAttributesVote(
      const std::u16string& password_value,
      FormStructure* form_structure) {
    // Don't crowdsource password attributes for non-ascii passwords.
    for (const auto& e : password_value) {
      if (!(IsUppercaseLetter(e) || IsLowercaseLetter(e) || IsNumeric(e) ||
            IsSpecialSymbol(e))) {
        return;
      }
    }
  
    // Select a character class attribute to upload. Upload special symbols more
    // often (8 in 9 cases) as most issues are due to missing or wrong special
    // symbols. Don't upload info about uppercase and numeric characters as all
    // sites that allow lowercase letters also uppercase letters, and all sites
    // allow numeric symbols in passwords.
    autofill::PasswordAttribute character_class_attribute;
    bool (*predicate)(int c) = nullptr;
    if (base::RandGenerator(9) == 0) {
      predicate = &IsLowercaseLetter;
      character_class_attribute =
          autofill::PasswordAttribute::kHasLowercaseLetter;
    } else {
      predicate = &IsSpecialSymbol;
      character_class_attribute = autofill::PasswordAttribute::kHasSpecialSymbol;
    }
  
    // Apply the randomized response technique to noisify the actual value
    // (https://en.wikipedia.org/wiki/Randomized_response).
    bool respond_randomly = base::RandGenerator(2);
    bool randomized_value_for_character_class =
        respond_randomly ? base::RandGenerator(2)
                         : base::ranges::any_of(password_value, predicate);
    form_structure->set_password_attributes_vote(std::make_pair(
        character_class_attribute, randomized_value_for_character_class));
  
    if (character_class_attribute ==
            autofill::PasswordAttribute::kHasSpecialSymbol &&
        randomized_value_for_character_class) {
      form_structure->set_password_symbol_vote(
          respond_randomly ? GetRandomSpecialSymbol()
                           : GetRandomSpecialSymbolFromPassword(password_value));
    }
  
    size_t actual_length = password_value.size();
    size_t randomized_length = actual_length <= 1 || base::RandGenerator(5) == 0
                                   ? actual_length
                                   : base::RandGenerator(actual_length - 1) + 1;
  
    form_structure->set_password_length_vote(randomized_length);
  }
  
  void VotesUploader::StoreInitialFieldValues(
      const autofill::FormData& observed_form) {
    for (const auto& field : observed_form.fields) {
      if (!field.value.empty()) {
        initial_values_.insert(
            std::make_pair(field.unique_renderer_id, field.value));
      }
    }
  }
  
  bool VotesUploader::StartUploadRequest(
      std::unique_ptr<autofill::FormStructure> form_to_upload,
      const ServerFieldTypeSet& available_field_types) {
    AutofillDownloadManager* download_manager =
        client_->GetAutofillDownloadManager();
    if (!download_manager)
      return false;
  
    // Force uploading as these events are relatively rare and we want to make
    // sure to receive them.
    form_to_upload->set_upload_required(UPLOAD_REQUIRED);
  
    // Attach the Randomized Encoder.
    form_to_upload->set_randomized_encoder(
        RandomizedEncoder::Create(client_->GetPrefs()));
  
    return download_manager->StartUploadRequest(
        *form_to_upload, false /* was_autofilled */, available_field_types,
        std::string(), true /* observed_submission */, nullptr /* prefs */);
  }
  
  void VotesUploader::SaveFieldVote(autofill::FormSignature form_signature,
                                    autofill::FieldSignature field_signature,
                                    autofill::ServerFieldType field_type) {
    FieldInfoManager* field_info_manager = client_->GetFieldInfoManager();
    if (!field_info_manager)
      return;
    field_info_manager->AddFieldType(form_signature, field_signature, field_type);
  }
  
  }  // namespace password_manager
  