| // 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/password_form_filling.h" |
| |
| #include "base/feature_list.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/metrics/user_metrics.h" |
| #include "components/autofill/core/common/autofill_util.h" |
| #include "components/autofill/core/common/password_form.h" |
| #include "components/autofill/core/common/password_form_fill_data.h" |
| #include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h" |
| #include "components/password_manager/core/browser/browser_save_password_progress_logger.h" |
| #include "components/password_manager/core/browser/password_form_metrics_recorder.h" |
| #include "components/password_manager/core/browser/password_manager_client.h" |
| #include "components/password_manager/core/browser/password_manager_driver.h" |
| #include "components/password_manager/core/browser/password_manager_util.h" |
| #include "components/password_manager/core/common/password_manager_features.h" |
| |
| using autofill::PasswordForm; |
| using autofill::PasswordFormFillData; |
| using Logger = autofill::SavePasswordProgressLogger; |
| |
| namespace password_manager { |
| |
| namespace { |
| bool PreferredRealmIsFromAndroid(const PasswordFormFillData& fill_data) { |
| return FacetURI::FromPotentiallyInvalidSpec(fill_data.preferred_realm) |
| .IsValidAndroidFacetURI(); |
| } |
| |
| bool ContainsAndroidCredentials(const PasswordFormFillData& fill_data) { |
| for (const auto& login : fill_data.additional_logins) { |
| if (FacetURI::FromPotentiallyInvalidSpec(login.second.realm) |
| .IsValidAndroidFacetURI()) { |
| return true; |
| } |
| } |
| |
| return PreferredRealmIsFromAndroid(fill_data); |
| } |
| |
| bool IsFillOnAccountSelectFeatureEnabled() { |
| return base::FeatureList::IsEnabled( |
| password_manager::features::kFillOnAccountSelect); |
| } |
| |
| void Autofill(PasswordManagerClient* client, |
| PasswordManagerDriver* driver, |
| const PasswordForm& form_for_autofill, |
| const std::vector<const PasswordForm*>& best_matches, |
| const std::vector<const PasswordForm*>& federated_matches, |
| const PasswordForm& preferred_match, |
| bool wait_for_username) { |
| DCHECK_EQ(PasswordForm::Scheme::kHtml, preferred_match.scheme); |
| |
| std::unique_ptr<BrowserSavePasswordProgressLogger> logger; |
| if (password_manager_util::IsLoggingActive(client)) { |
| logger.reset( |
| new BrowserSavePasswordProgressLogger(client->GetLogManager())); |
| logger->LogMessage(Logger::STRING_PASSWORDMANAGER_AUTOFILL); |
| } |
| |
| PasswordFormFillData fill_data(form_for_autofill, best_matches, |
| preferred_match, wait_for_username); |
| if (logger) |
| logger->LogBoolean(Logger::STRING_WAIT_FOR_USERNAME, wait_for_username); |
| UMA_HISTOGRAM_BOOLEAN( |
| "PasswordManager.FillSuggestionsIncludeAndroidAppCredentials", |
| ContainsAndroidCredentials(fill_data)); |
| metrics_util::LogFilledCredentialIsFromAndroidApp( |
| PreferredRealmIsFromAndroid(fill_data)); |
| driver->FillPasswordForm(fill_data); |
| |
| client->PasswordWasAutofilled(best_matches, form_for_autofill.origin, |
| &federated_matches); |
| } |
| |
| } // namespace |
| |
| LikelyFormFilling SendFillInformationToRenderer( |
| PasswordManagerClient* client, |
| PasswordManagerDriver* driver, |
| const PasswordForm& observed_form, |
| const std::vector<const PasswordForm*>& best_matches, |
| const std::vector<const PasswordForm*>& federated_matches, |
| const PasswordForm* preferred_match, |
| PasswordFormMetricsRecorder* metrics_recorder) { |
| DCHECK(driver); |
| DCHECK_EQ(PasswordForm::Scheme::kHtml, observed_form.scheme); |
| |
| if (autofill::IsShowAutofillSignaturesEnabled()) { |
| driver->AnnotateFieldsWithParsingResult( |
| {.username_renderer_id = observed_form.username_element_renderer_id, |
| .password_renderer_id = observed_form.password_element_renderer_id, |
| .new_password_renderer_id = |
| observed_form.new_password_element_renderer_id, |
| .confirm_password_renderer_id = |
| observed_form.confirmation_password_element_renderer_id}); |
| } |
| |
| if (best_matches.empty()) { |
| driver->InformNoSavedCredentials(); |
| metrics_recorder->RecordFillEvent( |
| PasswordFormMetricsRecorder::kManagerFillEventNoCredential); |
| return LikelyFormFilling::kNoFilling; |
| } |
| DCHECK(preferred_match); |
| |
| // If the parser of the PasswordFormManager decides that there is no |
| // current password field, no filling attempt will be made. In this case the |
| // renderer won't treat this as the "first filling" and won't record metrics |
| // accordingly. The browser should not do that either. |
| const bool no_sign_in_form = |
| !observed_form.HasPasswordElement() && !observed_form.IsSingleUsername(); |
| |
| // Wait for the username before filling passwords in case the |
| // FillOnAccountSelectHttp feature is active and the main frame is |
| // insecure. |
| const bool enable_foas_on_http = |
| base::FeatureList::IsEnabled(features::kFillOnAccountSelectHttp) && |
| !client->IsMainFrameSecure(); |
| |
| // Proceed to autofill. |
| // Note that we provide the choices but don't actually prefill a value if: |
| // (1) we are in Incognito mode, or |
| // (2) if it matched using public suffix domain matching, or |
| // (3) it would result in unexpected filling in a form with new password |
| // fields. |
| // (4) the current main frame origin is insecure and the FOAS on HTTP feature |
| // is active. |
| using WaitForUsernameReason = |
| PasswordFormMetricsRecorder::WaitForUsernameReason; |
| WaitForUsernameReason wait_for_username_reason = |
| WaitForUsernameReason::kDontWait; |
| if (client->IsIncognito()) { |
| wait_for_username_reason = WaitForUsernameReason::kIncognitoMode; |
| } else if (preferred_match->is_public_suffix_match) { |
| wait_for_username_reason = WaitForUsernameReason::kPublicSuffixMatch; |
| } else if (no_sign_in_form) { |
| // If the parser did not find a current password element, don't fill. |
| wait_for_username_reason = WaitForUsernameReason::kFormNotGoodForFilling; |
| } else if (enable_foas_on_http) { |
| wait_for_username_reason = WaitForUsernameReason::kFoasOnHTTP; |
| } else if (autofill::IsTouchToFillEnabled()) { |
| wait_for_username_reason = WaitForUsernameReason::kTouchToFill; |
| } else if (IsFillOnAccountSelectFeatureEnabled()) { |
| wait_for_username_reason = WaitForUsernameReason::kFoasFeature; |
| } |
| |
| // Record no "FirstWaitForUsernameReason" metrics for a form that is not meant |
| // for filling. The renderer won't record a "FirstFillingResult" either. |
| if (!no_sign_in_form) { |
| metrics_recorder->RecordFirstWaitForUsernameReason( |
| wait_for_username_reason); |
| } |
| |
| bool wait_for_username = |
| wait_for_username_reason != WaitForUsernameReason::kDontWait; |
| |
| if (wait_for_username) { |
| metrics_recorder->SetManagerAction( |
| PasswordFormMetricsRecorder::kManagerActionNone); |
| metrics_recorder->RecordFillEvent( |
| PasswordFormMetricsRecorder::kManagerFillEventBlockedOnInteraction); |
| } else { |
| metrics_recorder->SetManagerAction( |
| PasswordFormMetricsRecorder::kManagerActionAutofilled); |
| metrics_recorder->RecordFillEvent( |
| PasswordFormMetricsRecorder::kManagerFillEventAutofilled); |
| base::RecordAction(base::UserMetricsAction("PasswordManager_Autofilled")); |
| } |
| |
| // Continue with autofilling any password forms as traditionally has been |
| // done. |
| Autofill(client, driver, observed_form, best_matches, federated_matches, |
| *preferred_match, wait_for_username); |
| return wait_for_username ? LikelyFormFilling::kFillOnAccountSelect |
| : LikelyFormFilling::kFillOnPageLoad; |
| } |
| |
| } // namespace password_manager |