blob: dbb560c1bac277c6398d1b6892dde4a35edb08aa [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/password_manager/android/password_generation_controller_impl.h"
#include <map>
#include <utility>
#include "base/functional/callback.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_callback.h"
#include "chrome/browser/autofill/mock_manual_filling_controller.h"
#include "chrome/browser/password_manager/android/password_generation_dialog_view_interface.h"
#include "chrome/browser/password_manager/chrome_password_manager_client.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "components/autofill/core/browser/autofill_client.h"
#include "components/autofill/core/browser/test_autofill_client.h"
#include "components/autofill/core/browser/ui/accessory_sheet_enums.h"
#include "components/autofill/core/common/password_generation_util.h"
#include "components/password_manager/content/browser/content_password_manager_driver.h"
#include "components/password_manager/core/browser/mock_password_store_interface.h"
#include "components/password_manager/core/browser/password_autofill_manager.h"
#include "components/password_manager/core/browser/password_generation_frame_helper.h"
#include "components/password_manager/core/browser/password_manager.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/password_manager/core/browser/stub_password_manager_client.h"
#include "components/password_manager/core/browser/stub_password_manager_driver.h"
#include "components/password_manager/core/common/password_manager_features.h"
#include "content/public/browser/web_contents.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
using autofill::password_generation::PasswordGenerationType;
using password_manager::metrics_util::GenerationDialogChoice;
namespace {
using autofill::FooterCommand;
using autofill::mojom::FocusedFieldType;
using autofill::password_generation::PasswordGenerationUIData;
using base::ASCIIToUTF16;
using password_manager::ContentPasswordManagerDriver;
using password_manager::MockPasswordStoreInterface;
using password_manager::PasswordForm;
using testing::_;
using testing::AtMost;
using testing::ByMove;
using testing::Eq;
using testing::Mock;
using testing::NiceMock;
using testing::Return;
using testing::StrictMock;
using ShouldShowAction = ManualFillingController::ShouldShowAction;
class TestPasswordManagerClient
: public password_manager::StubPasswordManagerClient {
public:
TestPasswordManagerClient();
~TestPasswordManagerClient() override;
password_manager::PasswordStoreInterface* GetProfilePasswordStore()
const override;
MOCK_METHOD((const password_manager::PasswordManager*),
GetPasswordManager,
(),
(const, override));
private:
scoped_refptr<MockPasswordStoreInterface> mock_password_store_;
};
TestPasswordManagerClient::TestPasswordManagerClient() {
mock_password_store_ = new MockPasswordStoreInterface();
}
TestPasswordManagerClient::~TestPasswordManagerClient() = default;
password_manager::PasswordStoreInterface*
TestPasswordManagerClient::GetProfilePasswordStore() const {
return mock_password_store_.get();
}
// Mock modal dialog view used to bypass the need of a valid top level window.
class MockPasswordGenerationDialogView
: public PasswordGenerationDialogViewInterface {
public:
MockPasswordGenerationDialogView() = default;
MOCK_METHOD(void,
Show,
(std::u16string&,
base::WeakPtr<password_manager::ContentPasswordManagerDriver>,
PasswordGenerationType),
(override));
MOCK_METHOD(void, Destroy, (), ());
MockPasswordGenerationDialogView(const MockPasswordGenerationDialogView&) =
delete;
MockPasswordGenerationDialogView& operator=(
const MockPasswordGenerationDialogView&) = delete;
~MockPasswordGenerationDialogView() override { Destroy(); }
};
PasswordGenerationUIData GetTestGenerationUIData1() {
PasswordGenerationUIData data;
data.form_data.action = GURL("http://www.example1.com/accounts/Login");
data.form_data.url = GURL("http://www.example1.com/accounts/LoginAuth");
data.generation_element = u"testelement1";
data.max_length = 10;
return data;
}
PasswordGenerationUIData GetTestGenerationUIData2() {
PasswordGenerationUIData data;
data.form_data.action = GURL("http://www.example2.com/accounts/Login");
data.form_data.url = GURL("http://www.example2.com/accounts/LoginAuth");
data.generation_element = u"testelement2";
data.max_length = 10;
return data;
}
MATCHER_P(PointsToSameAddress, expected, "") {
return arg.get() == expected;
}
} // namespace
class PasswordGenerationControllerTest
: public ChromeRenderViewHostTestHarness {
public:
void SetUp() override {
ChromeRenderViewHostTestHarness::SetUp();
test_pwd_manager_client_ = std::make_unique<TestPasswordManagerClient>();
password_manager_ = std::make_unique<password_manager::PasswordManager>(
test_pwd_manager_client_.get());
ON_CALL(*test_pwd_manager_client_, GetPasswordManager())
.WillByDefault(Return(password_manager_.get()));
PasswordGenerationControllerImpl::CreateForWebContentsForTesting(
web_contents(), test_pwd_manager_client_.get(),
mock_manual_filling_controller_.AsWeakPtr(),
mock_dialog_factory_.Get());
password_manager_driver_ = std::make_unique<ContentPasswordManagerDriver>(
main_rfh(), test_pwd_manager_client_.get(), &test_autofill_client_);
another_password_manager_driver_ =
std::make_unique<ContentPasswordManagerDriver>(
main_rfh(), test_pwd_manager_client_.get(), &test_autofill_client_);
// TODO(crbug.com/969051): Remove once kAutofillKeyboardAccessory is
// enabled.
password_autofill_manager_ =
std::make_unique<password_manager::PasswordAutofillManager>(
password_manager_driver_.get(), &test_autofill_client_,
test_pwd_manager_client_.get());
mock_dialog_ =
std::make_unique<NiceMock<MockPasswordGenerationDialogView>>();
EXPECT_CALL(mock_manual_filling_controller_,
OnAccessoryActionAvailabilityChanged(
ShouldShowAction(false),
autofill::AccessoryAction::GENERATE_PASSWORD_AUTOMATIC));
controller()->FocusedInputChanged(
FocusedFieldType::kFillablePasswordField,
base::AsWeakPtr(password_manager_driver_.get()));
}
PasswordGenerationController* controller() {
return PasswordGenerationControllerImpl::FromWebContents(web_contents());
}
base::WeakPtr<password_manager::ContentPasswordManagerDriver>
active_driver() {
return base::AsWeakPtr(password_manager_driver_.get());
}
base::WeakPtr<password_manager::ContentPasswordManagerDriver>
non_active_driver() {
return base::AsWeakPtr(another_password_manager_driver_.get());
}
const base::MockCallback<
PasswordGenerationControllerImpl::CreateDialogFactory>&
mock_dialog_factory() {
return mock_dialog_factory_;
}
protected:
StrictMock<MockManualFillingController> mock_manual_filling_controller_;
std::unique_ptr<ContentPasswordManagerDriver> password_manager_driver_;
std::unique_ptr<ContentPasswordManagerDriver>
another_password_manager_driver_;
std::unique_ptr<NiceMock<MockPasswordGenerationDialogView>> mock_dialog_;
private:
NiceMock<
base::MockCallback<PasswordGenerationControllerImpl::CreateDialogFactory>>
mock_dialog_factory_;
std::unique_ptr<password_manager::PasswordManager> password_manager_;
std::unique_ptr<password_manager::PasswordAutofillManager>
password_autofill_manager_;
std::unique_ptr<TestPasswordManagerClient> test_pwd_manager_client_;
autofill::TestAutofillClient test_autofill_client_;
};
TEST_F(PasswordGenerationControllerTest, IsNotRecreatedForSameWebContents) {
PasswordGenerationController* initial_controller =
PasswordGenerationControllerImpl::FromWebContents(web_contents());
EXPECT_NE(nullptr, initial_controller);
PasswordGenerationControllerImpl::CreateForWebContents(web_contents());
EXPECT_EQ(PasswordGenerationControllerImpl::FromWebContents(web_contents()),
initial_controller);
}
TEST_F(PasswordGenerationControllerTest, RelaysAutomaticGenerationAvailable) {
EXPECT_CALL(mock_manual_filling_controller_,
OnAccessoryActionAvailabilityChanged(
ShouldShowAction(true),
autofill::AccessoryAction::GENERATE_PASSWORD_AUTOMATIC));
controller()->OnAutomaticGenerationAvailable(
active_driver(), GetTestGenerationUIData1(), gfx::RectF(100, 20));
}
// Tests that if AutomaticGenerationAvailable is called for different
// password forms, the form and field signatures used for password generation
// are updated.
TEST_F(PasswordGenerationControllerTest,
UpdatesSignaturesForDifferentGenerationForms) {
// Called twice for different forms.
EXPECT_CALL(mock_manual_filling_controller_,
OnAccessoryActionAvailabilityChanged(
ShouldShowAction(true),
autofill::AccessoryAction::GENERATE_PASSWORD_AUTOMATIC))
.Times(2);
controller()->OnAutomaticGenerationAvailable(
active_driver(), GetTestGenerationUIData1(), gfx::RectF(100, 20));
PasswordGenerationUIData new_ui_data = GetTestGenerationUIData2();
controller()->OnAutomaticGenerationAvailable(active_driver(), new_ui_data,
gfx::RectF(100, 20));
autofill::FormSignature form_signature =
autofill::CalculateFormSignature(new_ui_data.form_data);
autofill::FieldSignature field_signature =
autofill::CalculateFieldSignatureByNameAndType(
new_ui_data.generation_element, "password");
EXPECT_EQ(controller()->get_form_signature_for_testing(), form_signature);
EXPECT_EQ(controller()->get_field_signature_for_testing(), field_signature);
NiceMock<MockPasswordGenerationDialogView>* raw_dialog_view =
mock_dialog_.get();
EXPECT_CALL(mock_dialog_factory(), Run)
.WillOnce(Return(ByMove(std::move(mock_dialog_))));
EXPECT_CALL(*raw_dialog_view,
Show(_, PointsToSameAddress(password_manager_driver_.get()),
PasswordGenerationType::kAutomatic));
controller()->OnGenerationRequested(PasswordGenerationType::kAutomatic);
}
TEST_F(PasswordGenerationControllerTest,
RecordsGeneratedPasswordAcceptedAutomatic) {
base::HistogramTester histogram_tester;
EXPECT_CALL(mock_manual_filling_controller_,
OnAccessoryActionAvailabilityChanged(
ShouldShowAction(true),
autofill::AccessoryAction::GENERATE_PASSWORD_AUTOMATIC));
controller()->OnAutomaticGenerationAvailable(
active_driver(), GetTestGenerationUIData1(), gfx::RectF(100, 20));
EXPECT_CALL(mock_manual_filling_controller_,
OnAccessoryActionAvailabilityChanged(
ShouldShowAction(false),
autofill::AccessoryAction::GENERATE_PASSWORD_AUTOMATIC));
controller()->GeneratedPasswordAccepted(u"t3stp@ssw0rd", active_driver(),
PasswordGenerationType::kAutomatic);
histogram_tester.ExpectUniqueSample(
"KeyboardAccessory.GenerationDialogChoice.Automatic",
GenerationDialogChoice::kAccepted, 1);
}
TEST_F(PasswordGenerationControllerTest,
RecordsGeneratedPasswordRejectedAutomatic) {
base::HistogramTester histogram_tester;
EXPECT_CALL(mock_manual_filling_controller_,
OnAccessoryActionAvailabilityChanged(
ShouldShowAction(false),
autofill::AccessoryAction::GENERATE_PASSWORD_AUTOMATIC));
controller()->GeneratedPasswordRejected(PasswordGenerationType::kAutomatic);
histogram_tester.ExpectUniqueSample(
"KeyboardAccessory.GenerationDialogChoice.Automatic",
GenerationDialogChoice::kRejected, 1);
}
TEST_F(PasswordGenerationControllerTest,
RecordsGeneratedPasswordAcceptedManual) {
base::HistogramTester histogram_tester;
controller()->OnGenerationRequested(PasswordGenerationType::kManual);
EXPECT_CALL(mock_dialog_factory(), Run)
.WillOnce(Return(ByMove(std::move(mock_dialog_))));
controller()->ShowManualGenerationDialog(password_manager_driver_.get(),
GetTestGenerationUIData1());
EXPECT_CALL(mock_manual_filling_controller_,
OnAccessoryActionAvailabilityChanged(
ShouldShowAction(false),
autofill::AccessoryAction::GENERATE_PASSWORD_AUTOMATIC));
controller()->GeneratedPasswordAccepted(u"t3stp@ssw0rd", active_driver(),
PasswordGenerationType::kManual);
histogram_tester.ExpectUniqueSample(
"KeyboardAccessory.GenerationDialogChoice.Manual",
GenerationDialogChoice::kAccepted, 1);
}
TEST_F(PasswordGenerationControllerTest,
RecordsGeneratedPasswordRejectedManual) {
base::HistogramTester histogram_tester;
EXPECT_CALL(mock_manual_filling_controller_,
OnAccessoryActionAvailabilityChanged(
ShouldShowAction(false),
autofill::AccessoryAction::GENERATE_PASSWORD_AUTOMATIC));
controller()->GeneratedPasswordRejected(PasswordGenerationType::kManual);
histogram_tester.ExpectUniqueSample(
"KeyboardAccessory.GenerationDialogChoice.Manual",
GenerationDialogChoice::kRejected, 1);
}
TEST_F(PasswordGenerationControllerTest,
SetActiveFrameOnAutomaticGenerationAvailable) {
// TODO(crbug.com/1421753): Refactor PasswordGenerationController so that
// OnAccessoryActionAvailabilityChanged would be called only once. Right now
// it's called twice: the first call resets the manual filling controller
// status and the second one sets it according to the focused input.
EXPECT_CALL(mock_manual_filling_controller_,
OnAccessoryActionAvailabilityChanged(
_, autofill::AccessoryAction::GENERATE_PASSWORD_AUTOMATIC))
.Times(AtMost(2));
controller()->OnAutomaticGenerationAvailable(
non_active_driver(), GetTestGenerationUIData2(), gfx::RectF(100, 20));
}
TEST_F(PasswordGenerationControllerTest,
ResetStateWhenFocusChangesToNonPassword) {
EXPECT_CALL(mock_manual_filling_controller_,
OnAccessoryActionAvailabilityChanged(
ShouldShowAction(false),
autofill::AccessoryAction::GENERATE_PASSWORD_AUTOMATIC));
controller()->FocusedInputChanged(FocusedFieldType::kFillableUsernameField,
active_driver());
EXPECT_FALSE(controller()->GetActiveFrameDriver());
}
TEST_F(PasswordGenerationControllerTest,
ResetStateWhenFocusChangesToOtherFramePassword) {
EXPECT_CALL(mock_manual_filling_controller_,
OnAccessoryActionAvailabilityChanged(
ShouldShowAction(false),
autofill::AccessoryAction::GENERATE_PASSWORD_AUTOMATIC));
controller()->FocusedInputChanged(FocusedFieldType::kFillablePasswordField,
non_active_driver());
EXPECT_EQ(another_password_manager_driver_.get(),
controller()->GetActiveFrameDriver().get());
}
TEST_F(PasswordGenerationControllerTest, HidesDialogWhenFocusChanges) {
controller()->OnGenerationRequested(PasswordGenerationType::kManual);
NiceMock<MockPasswordGenerationDialogView>* raw_dialog_view =
mock_dialog_.get();
EXPECT_CALL(mock_dialog_factory(), Run)
.WillOnce(Return(ByMove(std::move(mock_dialog_))));
EXPECT_CALL(*raw_dialog_view,
Show(_, PointsToSameAddress(password_manager_driver_.get()),
PasswordGenerationType::kManual));
controller()->ShowManualGenerationDialog(password_manager_driver_.get(),
GetTestGenerationUIData1());
EXPECT_CALL(mock_manual_filling_controller_,
OnAccessoryActionAvailabilityChanged(
ShouldShowAction(false),
autofill::AccessoryAction::GENERATE_PASSWORD_AUTOMATIC));
EXPECT_CALL(*raw_dialog_view, Destroy());
controller()->FocusedInputChanged(FocusedFieldType::kFillableUsernameField,
non_active_driver());
Mock::VerifyAndClearExpectations(raw_dialog_view);
}
TEST_F(PasswordGenerationControllerTest, ShowManualDialogForActiveFrame) {
controller()->OnGenerationRequested(PasswordGenerationType::kManual);
NiceMock<MockPasswordGenerationDialogView>* raw_dialog_view =
mock_dialog_.get();
EXPECT_CALL(mock_dialog_factory(), Run)
.WillOnce(Return(ByMove(std::move(mock_dialog_))));
EXPECT_CALL(*raw_dialog_view,
Show(_, PointsToSameAddress(password_manager_driver_.get()),
PasswordGenerationType::kManual));
controller()->ShowManualGenerationDialog(password_manager_driver_.get(),
GetTestGenerationUIData1());
}
TEST_F(PasswordGenerationControllerTest,
RejectShowManualDialogForNonActiveFrame) {
EXPECT_CALL(mock_dialog_factory(), Run).Times(0);
controller()->ShowManualGenerationDialog(
another_password_manager_driver_.get(), GetTestGenerationUIData1());
}
TEST_F(PasswordGenerationControllerTest, DontShowDialogIfAlreadyShown) {
controller()->OnGenerationRequested(PasswordGenerationType::kManual);
NiceMock<MockPasswordGenerationDialogView>* raw_dialog_view =
mock_dialog_.get();
EXPECT_CALL(mock_dialog_factory(), Run)
.WillOnce(Return(ByMove(std::move(mock_dialog_))));
EXPECT_CALL(*raw_dialog_view,
Show(_, PointsToSameAddress(password_manager_driver_.get()),
PasswordGenerationType::kManual));
controller()->ShowManualGenerationDialog(password_manager_driver_.get(),
GetTestGenerationUIData1());
EXPECT_CALL(mock_dialog_factory(), Run).Times(0);
controller()->ShowManualGenerationDialog(password_manager_driver_.get(),
GetTestGenerationUIData1());
}
TEST_F(PasswordGenerationControllerTest, DontShowManualDialogIfFocusChanged) {
controller()->OnGenerationRequested(PasswordGenerationType::kManual);
EXPECT_CALL(mock_manual_filling_controller_,
OnAccessoryActionAvailabilityChanged(
ShouldShowAction(false),
autofill::AccessoryAction::GENERATE_PASSWORD_AUTOMATIC));
controller()->FocusedInputChanged(FocusedFieldType::kFillablePasswordField,
non_active_driver());
EXPECT_CALL(mock_dialog_factory(), Run).Times(0);
controller()->ShowManualGenerationDialog(password_manager_driver_.get(),
GetTestGenerationUIData1());
}
TEST_F(PasswordGenerationControllerTest,
DoesNotCallKeyboardAccessoryWhenGenerationBottomSheetRequired) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(
password_manager::features::kPasswordGenerationBottomSheet);
// Keyboard accessory shouldn't be called.
EXPECT_CALL(mock_manual_filling_controller_,
OnAccessoryActionAvailabilityChanged(
ShouldShowAction(true),
autofill::AccessoryAction::GENERATE_PASSWORD_AUTOMATIC))
.Times(0);
controller()->OnAutomaticGenerationAvailable(
active_driver(), GetTestGenerationUIData1(), gfx::RectF(100, 20));
controller()->HideBottomSheetIfNeeded();
}