// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

edition = "2023";

package optimization_guide.proto;

import "components/optimization_guide/proto/features/common_quality_data.proto";

import "components/optimization_guide/proto/model_quality_metadata.proto";

option optimize_for = LITE_RUNTIME;
option java_package = "org.chromium.components.optimization_guide.features.proto";

option java_outer_classname = "PasswordChangeSubmissionProto";

// DO NOT EDIT THIS FILE DIRECTLY!
//
// This file is generated in g3 and then synced to Chrome. Instead, please refer to
// http://go/chrome-mqls-onboarding (Google-internal link), and then changes will
// be synced with Chrome automatically.

message PasswordChangeSubmissionLoggingData {
  oneof quality_data {
    PasswordChangeQuality quality = 4;

    LoginAttemptOutcome login_attempt_outcome = 5;
  }

  PasswordChangeRequest request = 1 [features = { field_presence: EXPLICIT }];

  PasswordChangeResponse response = 2 [features = { field_presence: EXPLICIT }];

  ModelExecutionInfo model_execution_info = 3 [features = { field_presence: EXPLICIT }];
}

message PasswordChangeRequest {
  // The context of the page that the form is on.
  PageContext page_context = 1 [features = { field_presence: EXPLICIT }];

  // Step in which the APC flow is at. If this is not set, it is considered as
  // VERIFY_SUBMISSION_STEP to be backward compatible with older clients.
  FlowStep step = 2 [
    default = VERIFY_SUBMISSION_STEP,
    features = { field_presence: EXPLICIT }
  ];

  enum FlowStep {
    UNSPECIFIED_STEP = 0;

    // Before password change, use IDs [1...10]
    // Chrome needs to confirm the user is logged in on the main tab before
    // starting the APC flow in the background tab.
    IS_LOGGED_IN_STEP = 2;

    // Chrome wants to know if it is an actionable page and what needs to be
    // clicked to open the change password form.
    OPEN_FORM_STEP = 1;

    // Password change, use IDs [11...20]
    // Find ID of element to be clicked to submit the change password form.
    SUBMIT_FORM_STEP = 11;

    // After password change, use IDs [21...30]
    // After the form was submitted, identify if it was a successful submission
    // or not.
    VERIFY_SUBMISSION_STEP = 21;
  }
}

message PasswordChangeResponse {
  // The outcome of the form submission (Step=VERIFY_SUBMISSION_STEP).
  PasswordChangeSubmissionData outcome_data = 1 [features = { field_presence: EXPLICIT }];

  // The data of attempting to identify if the user is logged in
  // (Step=IS_LOGGED_IN_STEP).
  IsLoggedInResponseData is_logged_in_data = 5 [features = { field_presence: EXPLICIT }];

  // The data of attempting to identify what to click to open a change password
  // form (Step=OPEN_FORM_STEP).
  OpenFormResponseData open_form_data = 3 [features = { field_presence: EXPLICIT }];

  // The data on attempting to identify what to click to submit the
  // change password form (Step=SUBMIT_FORM_STEP).
  SubmitFormResponseData submit_form_data = 4 [features = { field_presence: EXPLICIT }];

  // Deprecated, response is in outcome_data.
  string model_output = 2 [
    deprecated = true,
    features = { field_presence: EXPLICIT }
  ];
}

message PasswordChangeSubmissionData {
  // The outcome of the password change submission.
  PasswordChangeOutcome submission_outcome = 1 [features = { field_presence: EXPLICIT }];

  // If the submission_outcome was UNSUCCESSFUL_OUTCOME, fill this with the
  // type.
  repeated PasswordChangeErrorCase error_case = 2;

  // Value of the message that made the model come up with the outcome and error
  // case.
  string submission_outcome_message = 3 [features = { field_presence: EXPLICIT }];

  enum PasswordChangeOutcome {
    UNKNOWN_OUTCOME = 0;

    // Password change was successful.
    SUCCESSFUL_OUTCOME = 1;

    // Password change failed, due to incorrect old password, mismatch in new
    // passwords or new password mismatching site requirements.
    UNSUCCESSFUL_OUTCOME = 2;

    // The user needs to intervene to complete the password change flow due to a
    // user authentication challenge (e.g. CAPTCHA, Robot Challenge, etc).
    USER_INTERVENTION_NEEDED = 3;
  }

  enum PasswordChangeErrorCase {
    UNKNOWN_CASE = 0;

    // Old password filled was incorrect.
    OLD_PASSWORD_INCORRECT = 1;

    // New and confirm new passwords don't match.
    PASSWORDS_DO_NOT_MATCH = 2;

    // New password doesn't match site requirements.
    NEW_PASSWORD_INCORRECT = 3;

    // There is another type of error in the page.
    PAGE_ERROR = 4;
  }
}

message IsLoggedInResponseData {
  // True if the user is logged in.
  bool is_logged_in = 1 [features = { field_presence: EXPLICIT }];

  // The status of the step.
  ErrorCase error_case = 2 [
    default = NO_ERROR,
    features = { field_presence: EXPLICIT }
  ];

  // LLM's thought process for determining the output.
  string thought = 3 [features = { field_presence: EXPLICIT }];

  enum ErrorCase {
    // No error.
    NO_ERROR = 0;

    // Login failed because of an incorrect password or username. This is a
    // terminal error and the flow should be aborted.
    LOGIN_FAILED = 1;

    // The page is a forgot password page. This indicates that the attempt to
    // log in was not successful. This is a terminal error and the flow should
    // be aborted.
    FORGOT_PASSWORD_PAGE = 2;
  }
}

message OpenFormResponseData {
  // Optional: Only filled if the page is SETTINGS_PAGE
  // The DOM node ID of the element to click to open the change pasword form.
  int32 dom_node_id_to_click = 1 [features = { field_presence: EXPLICIT }];

  // The type of page encountered after the navigation to the Change password
  // URL.
  PageType page_type = 2 [features = { field_presence: EXPLICIT }];

  // LLM's thought process for determining the output.
  string thought = 3 [features = { field_presence: EXPLICIT }];

  enum PageType {
    // Not an actionable page to continue the flow on.
    UNSPECIFIED_PAGE = 0;

    // The landed page is a Settings Page and an extra click
    // is needed in order to reach the change password form.
    SETTINGS_PAGE = 1;

    // The landed page is a Change Password form already.
    CHANGE_PASSWORD_FORM_PAGE = 2;

    // The landed page is a Login page.
    LOG_IN_PAGE = 3;

    // The landed page has an interruption (Captcha, Robot Challenge, etc)
    // that needs to be resolved manually by the user before proceeding.
    USER_INTERVENTION_NEEDED_PAGE = 4;
  }
}

message SubmitFormResponseData {
  // The DOM node ID of the element to click.
  int32 dom_node_id_to_click = 1 [features = { field_presence: EXPLICIT }];

  // For submitting the form, the user first needs to
  // resolve an interruption (Captcha, Robot Challenge
  // or authentication code) in order to submit the form.
  bool is_user_intervention_needed = 2 [features = { field_presence: EXPLICIT }];

  // LLM's thought process for determining the output.
  string thought = 3 [features = { field_presence: EXPLICIT }];
}

// Any data collected on the client outside of the model request and response.
// This includes user feedback, and metrics about feature usage.
message PasswordChangeQuality {
  // The model final status for cataloging a Successful Password Change
  // Submission.
  FinalModelStatus final_model_status = 1 [features = { field_presence: EXPLICIT }];

  // Deprecated, this is logged per step.
  int64 request_latency_ms = 2 [
    deprecated = true,
    features = { field_presence: EXPLICIT }
  ];

  // The total duration of the entire flow, from the moment the user accepts the
  // APC offer until the new password is saved.
  // Note: This is not the sum of the per-step latencies, as those
  // only take into account the request/response time for each individual step.
  int64 total_flow_time_ms = 11 [features = { field_presence: EXPLICIT }];

  // Quality data on step for checking if the user is logged in.
  StepQuality logged_in_check = 10 [features = { field_presence: EXPLICIT }];

  // Quality data on step for opening the change password form (this step may be
  // skipped on some sites).
  StepQuality open_form = 3 [features = { field_presence: EXPLICIT }];

  // Quality data on step for submitting a change password form.
  StepQuality submit_form = 4 [features = { field_presence: EXPLICIT }];

  // Quality data on step for verifying form submission.
  StepQuality verify_submission = 5 [features = { field_presence: EXPLICIT }];

  // Domain of the page.
  string domain = 6 [features = { field_presence: EXPLICIT }];

  // Location of user
  string location = 7 [features = { field_presence: EXPLICIT }];

  // Language of the page.
  string language = 8 [features = { field_presence: EXPLICIT }];

  // Whether this is a retry flow or not.
  bool is_retry = 9 [features = { field_presence: EXPLICIT }];

  // Information about the login form where leak check was triggered.
  FormData login_form_data = 12 [features = { field_presence: EXPLICIT }];

  // Whether the password which the user logged in with was in the password
  // store.
  bool was_password_stored = 13 [features = { field_presence: EXPLICIT }];

  // Information about the change password form encountered during the APC flow.
  FormData change_password_form_data = 14 [features = { field_presence: EXPLICIT }];

  // Information about forms that were parsed but discarded during the APC flow.
  repeated FormData discarded_forms_data = 15;

  // Requirements used to generate the new password. If the flow didn't reach
  // the password generation step, this field will be empty.
  PasswordRequirementsSpec password_requirements_spec = 16 [features = { field_presence: EXPLICIT }];

  message StepQuality {
    // The status of the step.
    SubmissionStatus status = 1 [
      default = UNKNOWN_STATUS,
      features = { field_presence: EXPLICIT }
    ];

    // Time it took for server to respond to the step.
    int64 request_latency_ms = 2 [features = { field_presence: EXPLICIT }];

    // Number of times the server call was retried for this step.
    // Currently only for 'is_logged_in' step there can be multiple calls.
    int64 retry_count = 3 [features = { field_presence: EXPLICIT }];

    // True if the model response was overridden by the user. Currently only
    // applicable to the 'is_logged_in' step, where the user can choose to
    // continue the flow even if the model thinks the user is not logged in.
    bool classification_overridden_by_user = 4 [
      default = false,
      features = { field_presence: EXPLICIT }
    ];

    // Represents the request and response for the current step.
    // In the existing structure, a single log entry is created for the entire
    // APC flow, which means only the final request and response are received.
    // To facilitate model quality analysis, it is necessary to capture the
    // request/response data for every step and associate them correctly.
    PasswordChangeRequest request = 5 [features = { field_presence: EXPLICIT }];

    PasswordChangeResponse response = 6 [features = { field_presence: EXPLICIT }];

    ModelExecutionInfo model_execution_info = 7 [features = { field_presence: EXPLICIT }];

    enum SubmissionStatus {
      // Response is empty or unparsable.
      UNKNOWN_STATUS = 0;

      // Not actionable, could not find element to click.
      ELEMENT_NOT_FOUND = 1;

      // Actionable element found and clicked.
      ACTION_SUCCESS = 2;

      // Form was not seen after clicking on the element given by LLM.
      FORM_NOT_FOUND = 3;

      // Previous step failed and took the flow to an unexpected state. E.g. the
      // input that was given to LLM to instruct on what to click was incorrect.
      UNEXPECTED_STATE = 4;

      // Flow was interrupted. This can happen in case of OTP/user closed the
      // tacrbug.com/user canceled the flow.
      FLOW_INTERRUPTED = 5;

      // Flow failure by LLM response (e.g. Verify Submission fails with
      // UNSUCCESSFUL).
      FAILURE_STATUS = 6;

      // An OTP was detected, causing an interruption in the flow.
      OTP_DETECTED = 7;

      // The step was skipped, e.g. change password form was already on a page,
      // thus no need to use LLM to open it.
      STEP_SKIPPED = 8;

      // The element was disabled, thus not clicked.
      ELEMENT_DISABLED = 9;

      // The element was offscreen, thus not clicked.
      ELEMENT_OFFSCREEN = 10;

      // The element was obscured, thus not clicked.
      ELEMENT_OBSCURED = 11;

      // Cross-origin navigation was detected during the step.
      CROSE_ORIGIN_NAVIGATION = 12;

      // Change password form filling failed. Applicable to SUBMIT_FORM_STEP
      // only.
      FORM_FILLING_FAILED = 13;

      // User intervention is needed due to authentication needed in
      // order to complete the flow, This was detected by the LLM.
      // This can be due to captcha, instructions sent by email, OTP or 2FA.
      USER_INTERVENTION_NEEDED = 14;

      // The step took too long to complete and an error was thrown.
      TIME_OUT = 15;
    }
  }

  message FormData {
    // Information about the fields in the form.
    repeated FieldData field_data = 1;

    // The signature of the form. This is the hash identifier used to denote
    // this form for query and voting purposes. See
    // components/autofill/core/common/signatures.cc for more details.
    uint64 form_signature = 2 [features = { field_presence: EXPLICIT }];

    // Form id attribute value.
    string form_id = 3 [features = { field_presence: EXPLICIT }];

    // Form name attribute value.
    string form_name = 4 [features = { field_presence: EXPLICIT }];

    // The URL (minus query parameters and fragment) containing the form.
    string url = 5 [features = { field_presence: EXPLICIT }];

    // Titles of form's buttons.
    repeated string button_text = 6;

    // If the form was discarded, mentions the reason why.
    DiscardReason discard_reason = 7 [features = { field_presence: EXPLICIT }];

    message FieldData {
      // The signature of the field. This is the hash identifier used to denote
      // this field for query and voting purposes. See
      // components/autofill/core/common/signatures.cc for more details.
      int64 signature = 1 [features = { field_presence: EXPLICIT }];

      // Field id attribute value.
      string id = 2 [features = { field_presence: EXPLICIT }];

      // Field name attribute value.
      string name = 3 [features = { field_presence: EXPLICIT }];

      // Field label attribute value.
      string label = 4 [features = { field_presence: EXPLICIT }];

      // Html tag of the field.
      string html_type = 5 [features = { field_presence: EXPLICIT }];

      // Placeholder for the field.
      string placeholder = 6 [features = { field_presence: EXPLICIT }];

      // The type of the field.
      FieldType field_type = 7 [features = { field_presence: EXPLICIT }];

      enum FieldType {
        // Assigned if the field is non of the types below.
        UNKNOWN = 0;

        // The field is a username field.
        USERNAME = 1;

        // The field is a password field.
        PASSWORD = 2;

        // The field is a new password field.
        NEW_PASSWORD = 3;

        // The field is a confirmation password field.
        CONFIRMATION_PASSWORD = 4;
      }
    }

    enum DiscardReason {
      UNKNOWN_REASON = 0;

      // New password field was not found.
      NO_NEW_PASSWORD_FIELD = 1;

      // New password field is disabled.
      NEW_PASSWORD_FIELD_DISABLED = 2;

      // Username field is empty and focusable. This is only a discard reason if
      // no confirmation or old password field is present.
      USERNAME_FIELD_EMPTY_AND_FOCUSABLE = 3;

      // The form contains a field that should be ignored.
      FIELD_TO_IGNORE = 4;

      // No driver associated with the form.
      NO_DRIVER = 5;

      // Form is not visible.
      FORM_NOT_VISIBLE = 6;
    }
  }
}


// Mirror of PasswordRequirementsSpec in
// components/autofill/core/browser/proto/password_requirements.proto in
// Chromium.
message PasswordRequirementsSpec {
  // The priority of this specification.
  //
  // Autofill uses crowdsourcing to learn the requirements of passwords.
  // It is possible to manually override the requirements for an entire domain
  // or for a specific form. The priority field can be used to determine which
  // specification wins. If multiple specifications are available, the one with
  // the highest priority value wins. In that case all other specifications
  // are discarded (i.e. completely overridden as if they were never there).
  //
  // Values:
  // - 10 default for crowd sourced data.
  // - 20 for hard-coded overrides by domain.
  // - 30 for hard-coded overrides for a specific form.
  uint32 priority = 1 [features = { field_presence: EXPLICIT }];

  // A version number to allow older versions of Chrome to notice that they
  // don't understand a new revision of the requirements spec.
  uint32 spec_version = 2 [features = { field_presence: EXPLICIT }];

  // Minimum and maximum length of generated passwords for a site. Note that
  // these defaults may override the required minimum occurrences of character
  // classes if the two contradict.
  //
  // Defaults may change over time.
  uint32 min_length = 4 [features = { field_presence: EXPLICIT }];

  // Maximum length of generated passwords for a site.
  uint32 max_length = 5 [features = { field_presence: EXPLICIT }];

  // The following fields allow overriding requirements for specific character
  // classes. If attributes of a CharacterClass are not set, the following
  // defaults apply:

  // All default character sets below are taken from 7-bit ASCII.

  // Default: character_set = [a-z], min = 1, max = MAX_INT32
  CharacterClass lower_case = 6 [features = { field_presence: EXPLICIT }];

  // Default: character_set = [A-Z], min = 1, max = MAX_INT32
  CharacterClass upper_case = 7 [features = { field_presence: EXPLICIT }];

  // Alphabetic should not be used together with lower_case and upper_case.
  // This is just an option for sites that don't differentiate the cases.
  // If you wish to use alphabetic, you should set lower_case and upper_case
  // to min = max = 0.
  // Default: character_set = [a-zA-Z], min = 0, max = 0
  CharacterClass alphabetic = 8 [features = { field_presence: EXPLICIT }];

  // Default: character_set = [0-9], min = 1, max = MAX_INT32
  CharacterClass numeric = 9 [features = { field_presence: EXPLICIT }];

  // Default: character_set = some default that works often
  // (e.g. [!@#$%^&*()_-+=]) but the default value may change over time!),
  // min = 0, max = 0
  CharacterClass symbols = 10 [features = { field_presence: EXPLICIT }];

  // A CharacterClass represents a type of characters (e.g. upper case,
  // lower case, numbers, special symbols, ...).
  //
  // With min and max it is possible to specify the desired frequency:
  // - Allowed: min = 0, max = MAX_INT32
  // - Required: min = 1, max = MAX_INT32
  // - Prohibited: min = 0, max = 0
  //
  // Note that character classes are meant to be handled independently of each
  // other. Therefore, it is recommended not to set max > 0 for two overlapping
  // character classes.
  //
  // It is also possible to express special rules like "at least two numbers"
  // via min = 2, max = MAX_INT32, or "only numbers" by setting min = max = 0
  // for all character classes.
  message CharacterClass {
    // This is the pool of characters that make up the character set. For
    // brevity, some defaults are assumed given the name/identity of the
    // character class member (i.e., lower case vs upper case).
    // Anything in UTF-8 is deemed acceptable here.
    string character_set = 1 [features = { field_presence: EXPLICIT }];

    // Minimum number of characters from this class.
    uint32 min = 2 [features = { field_presence: EXPLICIT }];

    // Maximum number of characters from this class.
    uint32 max = 3 [features = { field_presence: EXPLICIT }];
  }
}

// Recorded on the first attempt to login with a primary password or backup
// password after an APC flow.
message LoginAttemptOutcome {
  // True if the login was successful.
  bool success = 1 [features = { field_presence: EXPLICIT }];

  // Password type that was used for the login.
  PasswordType password_type = 2 [features = { field_presence: EXPLICIT }];

  // Domain of the page.
  string domain = 3 [features = { field_presence: EXPLICIT }];

  enum PasswordType {
    UNKNOWN = 0;

    // Login was done using a backup password.
    BACKUP = 1;

    // Login was done using the primary password.
    PRIMARY = 2;
  }
}
