Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(427)

Unified Diff: components/password_manager/content/browser/credential_manager_dispatcher_unittest.cc

Issue 1872133002: Revert of Reland: Switch components/password_manager code from IPC messages to Mojo. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: components/password_manager/content/browser/credential_manager_dispatcher_unittest.cc
diff --git a/components/password_manager/content/browser/credential_manager_dispatcher_unittest.cc b/components/password_manager/content/browser/credential_manager_dispatcher_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..75a26f189f38817f9b2c95ffd28fa9618d5dd60c
--- /dev/null
+++ b/components/password_manager/content/browser/credential_manager_dispatcher_unittest.cc
@@ -0,0 +1,1191 @@
+// Copyright 2014 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/content/browser/credential_manager_dispatcher.h"
+
+#include <stdint.h>
+
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/run_loop.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/thread_task_runner_handle.h"
+#include "components/password_manager/content/common/credential_manager_messages.h"
+#include "components/password_manager/core/browser/credential_manager_password_form_manager.h"
+#include "components/password_manager/core/browser/mock_affiliated_match_helper.h"
+#include "components/password_manager/core/browser/password_manager.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/browser/test_password_store.h"
+#include "components/password_manager/core/common/credential_manager_types.h"
+#include "components/password_manager/core/common/password_manager_pref_names.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/mock_render_process_host.h"
+#include "content/public/test/test_renderer_host.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using content::BrowserContext;
+using content::WebContents;
+
+using testing::_;
+
+namespace password_manager {
+
+namespace {
+
+// Chosen by fair dice roll. Guaranteed to be random.
+const int kRequestId = 4;
+
+const char kTestWebOrigin[] = "https://example.com/";
+const char kTestAndroidRealm1[] = "android://hash@com.example.one.android/";
+const char kTestAndroidRealm2[] = "android://hash@com.example.two.android/";
+
+class MockPasswordManagerClient : public StubPasswordManagerClient {
+ public:
+ MOCK_CONST_METHOD0(IsSavingAndFillingEnabledForCurrentPage, bool());
+ MOCK_CONST_METHOD0(IsOffTheRecord, bool());
+ MOCK_CONST_METHOD0(DidLastPageLoadEncounterSSLErrors, bool());
+ MOCK_METHOD1(NotifyUserAutoSigninPtr,
+ bool(const std::vector<autofill::PasswordForm*>& local_forms));
+ MOCK_METHOD1(NotifyUserCouldBeAutoSignedInPtr,
+ bool(autofill::PasswordForm* form));
+ MOCK_METHOD2(PromptUserToSavePasswordPtr,
+ void(PasswordFormManager*, CredentialSourceType type));
+ MOCK_METHOD4(PromptUserToChooseCredentialsPtr,
+ bool(const std::vector<autofill::PasswordForm*>& local_forms,
+ const std::vector<autofill::PasswordForm*>& federated_forms,
+ const GURL& origin,
+ const CredentialsCallback& callback));
+
+ explicit MockPasswordManagerClient(PasswordStore* store) : store_(store) {
+ prefs_.registry()->RegisterBooleanPref(prefs::kCredentialsEnableAutosignin,
+ true);
+ prefs_.registry()->RegisterBooleanPref(
+ prefs::kWasAutoSignInFirstRunExperienceShown, true);
+ }
+ ~MockPasswordManagerClient() override {}
+
+ bool PromptUserToSaveOrUpdatePassword(
+ std::unique_ptr<PasswordFormManager> manager,
+ CredentialSourceType type,
+ bool update_password) override {
+ manager_.swap(manager);
+ PromptUserToSavePasswordPtr(manager_.get(), type);
+ return true;
+ }
+
+ void NotifyUserCouldBeAutoSignedIn(
+ std::unique_ptr<autofill::PasswordForm> form) override {
+ NotifyUserCouldBeAutoSignedInPtr(form.get());
+ }
+
+ PasswordStore* GetPasswordStore() const override { return store_; }
+
+ PrefService* GetPrefs() override { return &prefs_; }
+
+ bool PromptUserToChooseCredentials(
+ ScopedVector<autofill::PasswordForm> local_forms,
+ ScopedVector<autofill::PasswordForm> federated_forms,
+ const GURL& origin,
+ const CredentialsCallback& callback) {
+ EXPECT_FALSE(local_forms.empty() && federated_forms.empty());
+ const autofill::PasswordForm* form =
+ local_forms.empty() ? federated_forms[0] : local_forms[0];
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, base::Owned(new autofill::PasswordForm(*form))));
+ PromptUserToChooseCredentialsPtr(local_forms.get(), federated_forms.get(),
+ origin, callback);
+ return true;
+ }
+
+ void NotifyUserAutoSignin(ScopedVector<autofill::PasswordForm> local_forms,
+ const GURL& origin) override {
+ EXPECT_FALSE(local_forms.empty());
+ NotifyUserAutoSigninPtr(local_forms.get());
+ }
+
+ PasswordFormManager* pending_manager() const { return manager_.get(); }
+
+ void set_zero_click_enabled(bool zero_click_enabled) {
+ prefs_.SetBoolean(prefs::kCredentialsEnableAutosignin, zero_click_enabled);
+ }
+
+ void set_first_run_seen(bool first_run_seen) {
+ prefs_.SetBoolean(prefs::kWasAutoSignInFirstRunExperienceShown,
+ first_run_seen);
+ }
+
+ private:
+ TestingPrefServiceSimple prefs_;
+ PasswordStore* store_;
+ std::unique_ptr<PasswordFormManager> manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockPasswordManagerClient);
+};
+
+class TestCredentialManagerDispatcher : public CredentialManagerDispatcher {
+ public:
+ TestCredentialManagerDispatcher(content::WebContents* web_contents,
+ PasswordManagerClient* client,
+ PasswordManagerDriver* driver);
+
+ private:
+ base::WeakPtr<PasswordManagerDriver> GetDriver() override;
+
+ base::WeakPtr<PasswordManagerDriver> driver_;
+};
+
+TestCredentialManagerDispatcher::TestCredentialManagerDispatcher(
+ content::WebContents* web_contents,
+ PasswordManagerClient* client,
+ PasswordManagerDriver* driver)
+ : CredentialManagerDispatcher(web_contents, client),
+ driver_(driver->AsWeakPtr()) {}
+
+base::WeakPtr<PasswordManagerDriver>
+TestCredentialManagerDispatcher::GetDriver() {
+ return driver_;
+}
+
+void RunAllPendingTasks() {
+ base::RunLoop run_loop;
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
+ run_loop.Run();
+}
+
+class SlightlyLessStubbyPasswordManagerDriver
+ : public StubPasswordManagerDriver {
+ public:
+ explicit SlightlyLessStubbyPasswordManagerDriver(
+ PasswordManagerClient* client)
+ : password_manager_(client) {}
+
+ PasswordManager* GetPasswordManager() override { return &password_manager_; }
+
+ private:
+ PasswordManager password_manager_;
+};
+
+} // namespace
+
+class CredentialManagerDispatcherTest
+ : public content::RenderViewHostTestHarness {
+ public:
+ CredentialManagerDispatcherTest() {}
+
+ void SetUp() override {
+ content::RenderViewHostTestHarness::SetUp();
+ store_ = new TestPasswordStore;
+ client_.reset(
+ new testing::NiceMock<MockPasswordManagerClient>(store_.get()));
+ stub_driver_.reset(
+ new SlightlyLessStubbyPasswordManagerDriver(client_.get()));
+ dispatcher_.reset(new TestCredentialManagerDispatcher(
+ web_contents(), client_.get(), stub_driver_.get()));
+ ON_CALL(*client_, IsSavingAndFillingEnabledForCurrentPage())
+ .WillByDefault(testing::Return(true));
+ ON_CALL(*client_, IsOffTheRecord()).WillByDefault(testing::Return(false));
+ ON_CALL(*client_, DidLastPageLoadEncounterSSLErrors())
+ .WillByDefault(testing::Return(false));
+
+ NavigateAndCommit(GURL("https://example.com/test.html"));
+
+ form_.username_value = base::ASCIIToUTF16("Username");
+ form_.display_name = base::ASCIIToUTF16("Display Name");
+ form_.password_value = base::ASCIIToUTF16("Password");
+ form_.origin = web_contents()->GetLastCommittedURL().GetOrigin();
+ form_.signon_realm = form_.origin.spec();
+ form_.scheme = autofill::PasswordForm::SCHEME_HTML;
+ form_.skip_zero_click = false;
+ form_.ssl_valid = true;
+
+ affiliated_form1_.username_value = base::ASCIIToUTF16("Affiliated 1");
+ affiliated_form1_.display_name = base::ASCIIToUTF16("Display Name");
+ affiliated_form1_.password_value = base::ASCIIToUTF16("Password");
+ affiliated_form1_.origin = GURL();
+ affiliated_form1_.signon_realm = kTestAndroidRealm1;
+ affiliated_form1_.scheme = autofill::PasswordForm::SCHEME_HTML;
+ affiliated_form1_.skip_zero_click = false;
+ affiliated_form1_.ssl_valid = true;
+
+ affiliated_form2_.username_value = base::ASCIIToUTF16("Affiliated 2");
+ affiliated_form2_.display_name = base::ASCIIToUTF16("Display Name");
+ affiliated_form2_.password_value = base::ASCIIToUTF16("Password");
+ affiliated_form2_.origin = GURL();
+ affiliated_form2_.signon_realm = kTestAndroidRealm2;
+ affiliated_form2_.scheme = autofill::PasswordForm::SCHEME_HTML;
+ affiliated_form2_.skip_zero_click = false;
+ affiliated_form2_.ssl_valid = true;
+
+ origin_path_form_.username_value = base::ASCIIToUTF16("Username 2");
+ origin_path_form_.display_name = base::ASCIIToUTF16("Display Name 2");
+ origin_path_form_.password_value = base::ASCIIToUTF16("Password 2");
+ origin_path_form_.origin = GURL("https://example.com/path");
+ origin_path_form_.signon_realm = origin_path_form_.origin.spec();
+ origin_path_form_.scheme = autofill::PasswordForm::SCHEME_HTML;
+ origin_path_form_.skip_zero_click = false;
+
+ cross_origin_form_.username_value = base::ASCIIToUTF16("Username");
+ cross_origin_form_.display_name = base::ASCIIToUTF16("Display Name");
+ cross_origin_form_.password_value = base::ASCIIToUTF16("Password");
+ cross_origin_form_.origin = GURL("https://example.net/");
+ cross_origin_form_.signon_realm = cross_origin_form_.origin.spec();
+ cross_origin_form_.scheme = autofill::PasswordForm::SCHEME_HTML;
+ cross_origin_form_.skip_zero_click = false;
+
+ store_->Clear();
+ EXPECT_TRUE(store_->IsEmpty());
+ }
+
+ void TearDown() override {
+ store_->ShutdownOnUIThread();
+ content::RenderViewHostTestHarness::TearDown();
+ }
+
+ void ExpectZeroClickSignInFailure() {
+ EXPECT_CALL(*client_, PromptUserToChooseCredentialsPtr(_, _, _, _))
+ .Times(testing::Exactly(0));
+ EXPECT_CALL(*client_, NotifyUserAutoSigninPtr(_))
+ .Times(testing::Exactly(0));
+
+ RunAllPendingTasks();
+
+ const uint32_t kMsgID = CredentialManagerMsg_SendCredential::ID;
+ const IPC::Message* message =
+ process()->sink().GetFirstMessageMatching(kMsgID);
+ ASSERT_TRUE(message);
+ CredentialManagerMsg_SendCredential::Param send_param;
+ CredentialManagerMsg_SendCredential::Read(message, &send_param);
+
+ EXPECT_EQ(CredentialType::CREDENTIAL_TYPE_EMPTY,
+ std::get<1>(send_param).type);
+ }
+
+ void ExpectZeroClickSignInSuccess(CredentialType type) {
+ EXPECT_CALL(*client_, PromptUserToChooseCredentialsPtr(_, _, _, _))
+ .Times(testing::Exactly(0));
+ EXPECT_CALL(*client_, NotifyUserAutoSigninPtr(_))
+ .Times(testing::Exactly(1));
+
+ RunAllPendingTasks();
+
+ const uint32_t kMsgID = CredentialManagerMsg_SendCredential::ID;
+ const IPC::Message* message =
+ process()->sink().GetFirstMessageMatching(kMsgID);
+ ASSERT_TRUE(message);
+ CredentialManagerMsg_SendCredential::Param send_param;
+ CredentialManagerMsg_SendCredential::Read(message, &send_param);
+
+ EXPECT_EQ(type, std::get<1>(send_param).type);
+ }
+
+ CredentialManagerDispatcher* dispatcher() { return dispatcher_.get(); }
+
+ protected:
+ autofill::PasswordForm form_;
+ autofill::PasswordForm affiliated_form1_;
+ autofill::PasswordForm affiliated_form2_;
+ autofill::PasswordForm origin_path_form_;
+ autofill::PasswordForm cross_origin_form_;
+ scoped_refptr<TestPasswordStore> store_;
+ std::unique_ptr<testing::NiceMock<MockPasswordManagerClient>> client_;
+ std::unique_ptr<SlightlyLessStubbyPasswordManagerDriver> stub_driver_;
+ std::unique_ptr<CredentialManagerDispatcher> dispatcher_;
+};
+
+TEST_F(CredentialManagerDispatcherTest, IsZeroClickAllowed) {
+ // IsZeroClickAllowed is uneffected by the first-run status.
+ client_->set_zero_click_enabled(true);
+ client_->set_first_run_seen(true);
+ EXPECT_TRUE(dispatcher()->IsZeroClickAllowed());
+
+ client_->set_zero_click_enabled(true);
+ client_->set_first_run_seen(false);
+ EXPECT_TRUE(dispatcher()->IsZeroClickAllowed());
+
+ client_->set_zero_click_enabled(false);
+ client_->set_first_run_seen(true);
+ EXPECT_FALSE(dispatcher()->IsZeroClickAllowed());
+
+ client_->set_zero_click_enabled(false);
+ client_->set_first_run_seen(false);
+ EXPECT_FALSE(dispatcher()->IsZeroClickAllowed());
+}
+
+TEST_F(CredentialManagerDispatcherTest, CredentialManagerOnStore) {
+ CredentialInfo info(form_, CredentialType::CREDENTIAL_TYPE_PASSWORD);
+ EXPECT_CALL(*client_, PromptUserToSavePasswordPtr(
+ _, CredentialSourceType::CREDENTIAL_SOURCE_API))
+ .Times(testing::Exactly(1));
+
+ dispatcher()->OnStore(kRequestId, info);
+
+ const uint32_t kMsgID = CredentialManagerMsg_AcknowledgeStore::ID;
+ const IPC::Message* message =
+ process()->sink().GetFirstMessageMatching(kMsgID);
+ EXPECT_TRUE(message);
+ process()->sink().ClearMessages();
+
+ // Allow the PasswordFormManager to talk to the password store, determine
+ // that the form is new, and set it as pending.
+ RunAllPendingTasks();
+
+ EXPECT_TRUE(client_->pending_manager()->HasCompletedMatching());
+
+ autofill::PasswordForm new_form =
+ client_->pending_manager()->pending_credentials();
+ EXPECT_EQ(form_.username_value, new_form.username_value);
+ EXPECT_EQ(form_.display_name, new_form.display_name);
+ EXPECT_EQ(form_.password_value, new_form.password_value);
+ EXPECT_EQ(form_.origin, new_form.origin);
+ EXPECT_EQ(form_.signon_realm, new_form.signon_realm);
+ EXPECT_EQ(autofill::PasswordForm::SCHEME_HTML, new_form.scheme);
+}
+
+TEST_F(CredentialManagerDispatcherTest, CredentialManagerStoreOverwrite) {
+ // Populate the PasswordStore with a form.
+ store_->AddLogin(form_);
+ RunAllPendingTasks();
+
+ // Calling 'OnStore' with a credential that matches |form_| should update
+ // the password without prompting the user.
+ CredentialInfo info(form_, CredentialType::CREDENTIAL_TYPE_PASSWORD);
+ info.password = base::ASCIIToUTF16("Totally new password.");
+ dispatcher()->OnStore(kRequestId, info);
+
+ EXPECT_CALL(*client_, PromptUserToSavePasswordPtr(
+ _, CredentialSourceType::CREDENTIAL_SOURCE_API))
+ .Times(testing::Exactly(0));
+
+ const uint32_t kMsgID = CredentialManagerMsg_AcknowledgeStore::ID;
+ const IPC::Message* message =
+ process()->sink().GetFirstMessageMatching(kMsgID);
+ EXPECT_TRUE(message);
+ process()->sink().ClearMessages();
+
+ // Allow the PasswordFormManager to talk to the password store, determine
+ // the form is a match for an existing form, and update the PasswordStore.
+ RunAllPendingTasks();
+
+ TestPasswordStore::PasswordMap passwords = store_->stored_passwords();
+ EXPECT_EQ(1U, passwords.size());
+ EXPECT_EQ(1U, passwords[form_.signon_realm].size());
+ EXPECT_EQ(base::ASCIIToUTF16("Totally new password."),
+ passwords[form_.signon_realm][0].password_value);
+}
+
+TEST_F(CredentialManagerDispatcherTest,
+ CredentialManagerStoreOverwriteZeroClick) {
+ // Set the global zero click flag on, and populate the PasswordStore with a
+ // form that's set to skip zero click.
+ client_->set_zero_click_enabled(true);
+ client_->set_first_run_seen(true);
+ form_.skip_zero_click = true;
+ store_->AddLogin(form_);
+ RunAllPendingTasks();
+
+ // Calling 'OnStore' with a credential that matches |form_| should update
+ // the credential without prompting the user.
+ CredentialInfo info(form_, CredentialType::CREDENTIAL_TYPE_PASSWORD);
+ dispatcher()->OnStore(kRequestId, info);
+ process()->sink().ClearMessages();
+
+ // Allow the PasswordFormManager to talk to the password store, determine
+ // the form is a match for an existing form, and update the PasswordStore.
+ RunAllPendingTasks();
+
+ // Verify that the update toggled the skip_zero_click flag off.
+ TestPasswordStore::PasswordMap passwords = store_->stored_passwords();
+ EXPECT_FALSE(passwords[form_.signon_realm][0].skip_zero_click);
+}
+
+TEST_F(CredentialManagerDispatcherTest,
+ CredentialManagerFederatedStoreOverwriteZeroClick) {
+ // Set the global zero click flag on, and populate the PasswordStore with a
+ // form that's set to skip zero click.
+ client_->set_zero_click_enabled(true);
+ client_->set_first_run_seen(true);
+ form_.federation_origin = url::Origin(GURL("https://example.com/"));
+ form_.skip_zero_click = true;
+ form_.signon_realm = "federation://example.com/example.com";
+ store_->AddLogin(form_);
+ RunAllPendingTasks();
+
+ // Calling 'OnStore' with a credential that matches |form_| should update
+ // the credential without prompting the user.
+ CredentialInfo info(form_, CredentialType::CREDENTIAL_TYPE_FEDERATED);
+ dispatcher()->OnStore(kRequestId, info);
+ process()->sink().ClearMessages();
+
+ // Allow the PasswordFormManager to talk to the password store, determine
+ // the form is a match for an existing form, and update the PasswordStore.
+ RunAllPendingTasks();
+
+ // Verify that the update toggled the skip_zero_click flag off.
+ TestPasswordStore::PasswordMap passwords = store_->stored_passwords();
+ EXPECT_FALSE(passwords[form_.signon_realm][0].skip_zero_click);
+}
+
+TEST_F(CredentialManagerDispatcherTest,
+ CredentialManagerGetOverwriteZeroClick) {
+ // Set the global zero click flag on, and populate the PasswordStore with a
+ // form that's set to skip zero click and has a primary key that won't match
+ // credentials initially created via `store()`.
+ client_->set_zero_click_enabled(true);
+ form_.skip_zero_click = true;
+ form_.username_element = base::ASCIIToUTF16("username-element");
+ form_.password_element = base::ASCIIToUTF16("password-element");
+ form_.signon_realm = "this is a realm";
+ form_.origin = GURL("https://example.com/old_form.html");
+ store_->AddLogin(form_);
+ RunAllPendingTasks();
+
+ std::vector<GURL> federations;
+ EXPECT_CALL(*client_, PromptUserToChooseCredentialsPtr(_, _, _, _))
+ .Times(testing::Exactly(1));
+ EXPECT_CALL(*client_, NotifyUserAutoSigninPtr(_)).Times(testing::Exactly(0));
+
+ dispatcher()->OnRequestCredential(kRequestId, false, true, federations);
+
+ RunAllPendingTasks();
+
+ const uint32_t kMsgID = CredentialManagerMsg_SendCredential::ID;
+ const IPC::Message* message =
+ process()->sink().GetFirstMessageMatching(kMsgID);
+ EXPECT_TRUE(message);
+
+ // Verify that the update toggled the skip_zero_click flag.
+ TestPasswordStore::PasswordMap passwords = store_->stored_passwords();
+ EXPECT_FALSE(passwords[form_.signon_realm][0].skip_zero_click);
+}
+
+TEST_F(CredentialManagerDispatcherTest,
+ CredentialManagerSignInWithSavingDisabledForCurrentPage) {
+ CredentialInfo info(form_, CredentialType::CREDENTIAL_TYPE_PASSWORD);
+ EXPECT_CALL(*client_, IsSavingAndFillingEnabledForCurrentPage())
+ .WillRepeatedly(testing::Return(false));
+ EXPECT_CALL(*client_, PromptUserToSavePasswordPtr(
+ _, CredentialSourceType::CREDENTIAL_SOURCE_API))
+ .Times(testing::Exactly(0));
+
+ dispatcher()->OnStore(kRequestId, info);
+
+ const uint32_t kMsgID = CredentialManagerMsg_AcknowledgeStore::ID;
+ const IPC::Message* message =
+ process()->sink().GetFirstMessageMatching(kMsgID);
+ EXPECT_TRUE(message);
+ process()->sink().ClearMessages();
+
+ RunAllPendingTasks();
+
+ EXPECT_FALSE(client_->pending_manager());
+}
+
+TEST_F(CredentialManagerDispatcherTest,
+ CredentialManagerOnRequireUserMediation) {
+ store_->AddLogin(form_);
+ store_->AddLogin(cross_origin_form_);
+ RunAllPendingTasks();
+
+ TestPasswordStore::PasswordMap passwords = store_->stored_passwords();
+ EXPECT_EQ(2U, passwords.size());
+ EXPECT_EQ(1U, passwords[form_.signon_realm].size());
+ EXPECT_EQ(1U, passwords[cross_origin_form_.signon_realm].size());
+ EXPECT_FALSE(passwords[form_.signon_realm][0].skip_zero_click);
+ EXPECT_FALSE(passwords[cross_origin_form_.signon_realm][0].skip_zero_click);
+
+ dispatcher()->OnRequireUserMediation(kRequestId);
+ RunAllPendingTasks();
+
+ const uint32_t kMsgID =
+ CredentialManagerMsg_AcknowledgeRequireUserMediation::ID;
+ const IPC::Message* message =
+ process()->sink().GetFirstMessageMatching(kMsgID);
+ EXPECT_TRUE(message);
+ process()->sink().ClearMessages();
+
+ passwords = store_->stored_passwords();
+ EXPECT_EQ(2U, passwords.size());
+ EXPECT_EQ(1U, passwords[form_.signon_realm].size());
+ EXPECT_EQ(1U, passwords[cross_origin_form_.signon_realm].size());
+ EXPECT_TRUE(passwords[form_.signon_realm][0].skip_zero_click);
+ EXPECT_FALSE(passwords[cross_origin_form_.signon_realm][0].skip_zero_click);
+}
+
+TEST_F(CredentialManagerDispatcherTest,
+ CredentialManagerOnRequireUserMediationIncognito) {
+ EXPECT_CALL(*client_, IsOffTheRecord()).WillRepeatedly(testing::Return(true));
+ store_->AddLogin(form_);
+ RunAllPendingTasks();
+
+ TestPasswordStore::PasswordMap passwords = store_->stored_passwords();
+ ASSERT_EQ(1U, passwords.size());
+ ASSERT_EQ(1U, passwords[form_.signon_realm].size());
+ EXPECT_FALSE(passwords[form_.signon_realm][0].skip_zero_click);
+
+ dispatcher()->OnRequireUserMediation(kRequestId);
+ RunAllPendingTasks();
+
+ const uint32_t kMsgID =
+ CredentialManagerMsg_AcknowledgeRequireUserMediation::ID;
+ const IPC::Message* message =
+ process()->sink().GetFirstMessageMatching(kMsgID);
+ EXPECT_TRUE(message);
+ process()->sink().ClearMessages();
+
+ passwords = store_->stored_passwords();
+ ASSERT_EQ(1U, passwords.size());
+ ASSERT_EQ(1U, passwords[form_.signon_realm].size());
+ EXPECT_FALSE(passwords[form_.signon_realm][0].skip_zero_click);
+}
+
+TEST_F(CredentialManagerDispatcherTest,
+ CredentialManagerOnRequireUserMediationWithAffiliation) {
+ store_->AddLogin(form_);
+ store_->AddLogin(cross_origin_form_);
+ store_->AddLogin(affiliated_form1_);
+ store_->AddLogin(affiliated_form2_);
+
+ auto mock_helper = base::WrapUnique(new MockAffiliatedMatchHelper);
+ store_->SetAffiliatedMatchHelper(std::move(mock_helper));
+
+ std::vector<GURL> federations;
+ std::vector<std::string> affiliated_realms;
+ affiliated_realms.push_back(kTestAndroidRealm1);
+ static_cast<MockAffiliatedMatchHelper*>(store_->affiliated_match_helper())
+ ->ExpectCallToGetAffiliatedAndroidRealms(
+ dispatcher_->GetSynthesizedFormForOrigin(), affiliated_realms);
+ RunAllPendingTasks();
+
+ TestPasswordStore::PasswordMap passwords = store_->stored_passwords();
+ EXPECT_EQ(4U, passwords.size());
+ EXPECT_FALSE(passwords[form_.signon_realm][0].skip_zero_click);
+ EXPECT_FALSE(passwords[cross_origin_form_.signon_realm][0].skip_zero_click);
+ EXPECT_FALSE(passwords[affiliated_form1_.signon_realm][0].skip_zero_click);
+ EXPECT_FALSE(passwords[affiliated_form2_.signon_realm][0].skip_zero_click);
+
+ dispatcher()->OnRequireUserMediation(kRequestId);
+ RunAllPendingTasks();
+ process()->sink().ClearMessages();
+
+ passwords = store_->stored_passwords();
+ EXPECT_EQ(4U, passwords.size());
+ EXPECT_TRUE(passwords[form_.signon_realm][0].skip_zero_click);
+ EXPECT_FALSE(passwords[cross_origin_form_.signon_realm][0].skip_zero_click);
+ EXPECT_TRUE(passwords[affiliated_form1_.signon_realm][0].skip_zero_click);
+ EXPECT_FALSE(passwords[affiliated_form2_.signon_realm][0].skip_zero_click);
+}
+
+TEST_F(CredentialManagerDispatcherTest,
+ CredentialManagerOnRequestCredentialWithEmptyPasswordStore) {
+ std::vector<GURL> federations;
+ EXPECT_CALL(*client_, PromptUserToSavePasswordPtr(
+ _, CredentialSourceType::CREDENTIAL_SOURCE_API))
+ .Times(testing::Exactly(0));
+ EXPECT_CALL(*client_, PromptUserToChooseCredentialsPtr(_, _, _, _))
+ .Times(testing::Exactly(0));
+ EXPECT_CALL(*client_, NotifyUserAutoSigninPtr(_)).Times(testing::Exactly(0));
+
+ dispatcher()->OnRequestCredential(kRequestId, false, true, federations);
+
+ RunAllPendingTasks();
+
+ const uint32_t kMsgID = CredentialManagerMsg_SendCredential::ID;
+ const IPC::Message* message =
+ process()->sink().GetFirstMessageMatching(kMsgID);
+ EXPECT_TRUE(message);
+ CredentialManagerMsg_SendCredential::Param param;
+ CredentialManagerMsg_SendCredential::Read(message, &param);
+ EXPECT_EQ(CredentialType::CREDENTIAL_TYPE_EMPTY, std::get<1>(param).type);
+ process()->sink().ClearMessages();
+}
+
+TEST_F(CredentialManagerDispatcherTest,
+ CredentialManagerOnRequestCredentialWithCrossOriginPasswordStore) {
+ store_->AddLogin(cross_origin_form_);
+
+ std::vector<GURL> federations;
+ EXPECT_CALL(*client_, PromptUserToSavePasswordPtr(
+ _, CredentialSourceType::CREDENTIAL_SOURCE_API))
+ .Times(testing::Exactly(0));
+ EXPECT_CALL(*client_, PromptUserToChooseCredentialsPtr(_, _, _, _))
+ .Times(testing::Exactly(0));
+ EXPECT_CALL(*client_, NotifyUserAutoSigninPtr(_)).Times(testing::Exactly(0));
+
+ dispatcher()->OnRequestCredential(kRequestId, false, true, federations);
+
+ RunAllPendingTasks();
+
+ const uint32_t kMsgID = CredentialManagerMsg_SendCredential::ID;
+ const IPC::Message* message =
+ process()->sink().GetFirstMessageMatching(kMsgID);
+ EXPECT_TRUE(message);
+ CredentialManagerMsg_SendCredential::Param param;
+ CredentialManagerMsg_SendCredential::Read(message, &param);
+ EXPECT_EQ(CredentialType::CREDENTIAL_TYPE_EMPTY, std::get<1>(param).type);
+ process()->sink().ClearMessages();
+}
+
+TEST_F(CredentialManagerDispatcherTest,
+ CredentialManagerOnRequestCredentialWithFullPasswordStore) {
+ client_->set_zero_click_enabled(false);
+ store_->AddLogin(form_);
+
+ std::vector<GURL> federations;
+ EXPECT_CALL(*client_, PromptUserToChooseCredentialsPtr(_, _, _, _))
+ .Times(testing::Exactly(1));
+ EXPECT_CALL(*client_, NotifyUserAutoSigninPtr(_)).Times(testing::Exactly(0));
+
+ dispatcher()->OnRequestCredential(kRequestId, false, true, federations);
+
+ RunAllPendingTasks();
+
+ const uint32_t kMsgID = CredentialManagerMsg_SendCredential::ID;
+ const IPC::Message* message =
+ process()->sink().GetFirstMessageMatching(kMsgID);
+ EXPECT_TRUE(message);
+}
+
+TEST_F(
+ CredentialManagerDispatcherTest,
+ CredentialManagerOnRequestCredentialWithZeroClickOnlyEmptyPasswordStore) {
+ std::vector<GURL> federations;
+ EXPECT_CALL(*client_, PromptUserToChooseCredentialsPtr(_, _, _, _))
+ .Times(testing::Exactly(0));
+ EXPECT_CALL(*client_, NotifyUserAutoSigninPtr(_)).Times(testing::Exactly(0));
+
+ dispatcher()->OnRequestCredential(kRequestId, true, true, federations);
+
+ RunAllPendingTasks();
+
+ const uint32_t kMsgID = CredentialManagerMsg_SendCredential::ID;
+ const IPC::Message* message =
+ process()->sink().GetFirstMessageMatching(kMsgID);
+ EXPECT_TRUE(message);
+ CredentialManagerMsg_SendCredential::Param send_param;
+ CredentialManagerMsg_SendCredential::Read(message, &send_param);
+ EXPECT_EQ(CredentialType::CREDENTIAL_TYPE_EMPTY,
+ std::get<1>(send_param).type);
+}
+
+TEST_F(CredentialManagerDispatcherTest,
+ CredentialManagerOnRequestCredentialWithZeroClickOnlyFullPasswordStore) {
+ store_->AddLogin(form_);
+ client_->set_first_run_seen(true);
+
+ std::vector<GURL> federations;
+
+ EXPECT_CALL(*client_, NotifyUserCouldBeAutoSignedInPtr(_)).Times(0);
+ dispatcher()->OnRequestCredential(kRequestId, true, true, federations);
+
+ ExpectZeroClickSignInSuccess(CredentialType::CREDENTIAL_TYPE_PASSWORD);
+}
+
+TEST_F(CredentialManagerDispatcherTest,
+ CredentialManagerOnRequestCredentialWithoutPasswords) {
+ store_->AddLogin(form_);
+ client_->set_first_run_seen(true);
+
+ std::vector<GURL> federations;
+
+ EXPECT_CALL(*client_, NotifyUserCouldBeAutoSignedInPtr(_)).Times(0);
+ dispatcher()->OnRequestCredential(kRequestId, true, false, federations);
+
+ ExpectZeroClickSignInFailure();
+}
+
+TEST_F(CredentialManagerDispatcherTest,
+ CredentialManagerOnRequestCredentialFederatedMatch) {
+ form_.federation_origin = url::Origin(GURL("https://example.com/"));
+ store_->AddLogin(form_);
+ client_->set_first_run_seen(true);
+
+ std::vector<GURL> federations;
+ federations.push_back(GURL("https://example.com/"));
+
+ EXPECT_CALL(*client_, NotifyUserCouldBeAutoSignedInPtr(_)).Times(0);
+ dispatcher()->OnRequestCredential(kRequestId, true, true, federations);
+
+ ExpectZeroClickSignInSuccess(CredentialType::CREDENTIAL_TYPE_FEDERATED);
+}
+
+TEST_F(CredentialManagerDispatcherTest,
+ CredentialManagerOnRequestCredentialFederatedNoMatch) {
+ form_.federation_origin = url::Origin(GURL("https://example.com/"));
+ store_->AddLogin(form_);
+ client_->set_first_run_seen(true);
+
+ std::vector<GURL> federations;
+ federations.push_back(GURL("https://not-example.com/"));
+
+ EXPECT_CALL(*client_, NotifyUserCouldBeAutoSignedInPtr(_)).Times(0);
+ dispatcher()->OnRequestCredential(kRequestId, true, true, federations);
+
+ ExpectZeroClickSignInFailure();
+}
+
+TEST_F(CredentialManagerDispatcherTest,
+ CredentialManagerOnRequestCredentialAffiliatedPasswordMatch) {
+ store_->AddLogin(affiliated_form1_);
+ client_->set_first_run_seen(true);
+ auto mock_helper = base::WrapUnique(new MockAffiliatedMatchHelper);
+ store_->SetAffiliatedMatchHelper(std::move(mock_helper));
+
+ std::vector<GURL> federations;
+ std::vector<std::string> affiliated_realms;
+ affiliated_realms.push_back(kTestAndroidRealm1);
+ static_cast<MockAffiliatedMatchHelper*>(store_->affiliated_match_helper())
+ ->ExpectCallToGetAffiliatedAndroidRealms(
+ dispatcher_->GetSynthesizedFormForOrigin(), affiliated_realms);
+
+ // We pass in 'true' for the 'include_passwords' argument to ensure that
+ // password-type credentials are included as potential matches.
+ dispatcher()->OnRequestCredential(kRequestId, true, true, federations);
+
+ ExpectZeroClickSignInSuccess(CredentialType::CREDENTIAL_TYPE_PASSWORD);
+}
+
+TEST_F(CredentialManagerDispatcherTest,
+ CredentialManagerOnRequestCredentialAffiliatedPasswordNoMatch) {
+ store_->AddLogin(affiliated_form1_);
+ client_->set_first_run_seen(true);
+ auto mock_helper = base::WrapUnique(new MockAffiliatedMatchHelper);
+ store_->SetAffiliatedMatchHelper(std::move(mock_helper));
+
+ std::vector<GURL> federations;
+ std::vector<std::string> affiliated_realms;
+ affiliated_realms.push_back(kTestAndroidRealm1);
+ static_cast<MockAffiliatedMatchHelper*>(store_->affiliated_match_helper())
+ ->ExpectCallToGetAffiliatedAndroidRealms(
+ dispatcher_->GetSynthesizedFormForOrigin(), affiliated_realms);
+
+ // We pass in 'false' for the 'include_passwords' argument to ensure that
+ // password-type credentials are excluded as potential matches.
+ dispatcher()->OnRequestCredential(kRequestId, true, false, federations);
+
+ ExpectZeroClickSignInFailure();
+}
+
+TEST_F(CredentialManagerDispatcherTest,
+ CredentialManagerOnRequestCredentialAffiliatedFederatedMatch) {
+ affiliated_form1_.federation_origin =
+ url::Origin(GURL("https://example.com/"));
+ store_->AddLogin(affiliated_form1_);
+ client_->set_first_run_seen(true);
+ auto mock_helper = base::WrapUnique(new MockAffiliatedMatchHelper);
+ store_->SetAffiliatedMatchHelper(std::move(mock_helper));
+
+ std::vector<GURL> federations;
+ federations.push_back(GURL("https://example.com/"));
+
+ std::vector<std::string> affiliated_realms;
+ affiliated_realms.push_back(kTestAndroidRealm1);
+ static_cast<MockAffiliatedMatchHelper*>(store_->affiliated_match_helper())
+ ->ExpectCallToGetAffiliatedAndroidRealms(
+ dispatcher_->GetSynthesizedFormForOrigin(), affiliated_realms);
+
+ dispatcher()->OnRequestCredential(kRequestId, true, true, federations);
+
+ ExpectZeroClickSignInSuccess(CredentialType::CREDENTIAL_TYPE_FEDERATED);
+}
+
+TEST_F(CredentialManagerDispatcherTest,
+ CredentialManagerOnRequestCredentialAffiliatedFederatedNoMatch) {
+ affiliated_form1_.federation_origin =
+ url::Origin(GURL("https://example.com/"));
+ store_->AddLogin(affiliated_form1_);
+ client_->set_first_run_seen(true);
+ auto mock_helper = base::WrapUnique(new MockAffiliatedMatchHelper);
+ store_->SetAffiliatedMatchHelper(std::move(mock_helper));
+
+ std::vector<GURL> federations;
+ federations.push_back(GURL("https://not-example.com/"));
+
+ std::vector<std::string> affiliated_realms;
+ affiliated_realms.push_back(kTestAndroidRealm1);
+ static_cast<MockAffiliatedMatchHelper*>(store_->affiliated_match_helper())
+ ->ExpectCallToGetAffiliatedAndroidRealms(
+ dispatcher_->GetSynthesizedFormForOrigin(), affiliated_realms);
+
+ dispatcher()->OnRequestCredential(kRequestId, true, true, federations);
+
+ ExpectZeroClickSignInFailure();
+}
+
+TEST_F(CredentialManagerDispatcherTest, RequestCredentialWithoutFirstRun) {
+ client_->set_first_run_seen(false);
+
+ store_->AddLogin(form_);
+
+ std::vector<GURL> federations;
+ EXPECT_CALL(*client_,
+ NotifyUserCouldBeAutoSignedInPtr(testing::Pointee(form_)))
+ .Times(1);
+ dispatcher()->OnRequestCredential(kRequestId, true, true, federations);
+
+ ExpectZeroClickSignInFailure();
+}
+
+TEST_F(CredentialManagerDispatcherTest, RequestCredentialWithFirstRunAndSkip) {
+ client_->set_first_run_seen(true);
+
+ form_.skip_zero_click = true;
+ store_->AddLogin(form_);
+
+ std::vector<GURL> federations;
+ EXPECT_CALL(*client_,
+ NotifyUserCouldBeAutoSignedInPtr(testing::Pointee(form_)))
+ .Times(1);
+ dispatcher()->OnRequestCredential(kRequestId, true, true, federations);
+
+ ExpectZeroClickSignInFailure();
+}
+
+TEST_F(CredentialManagerDispatcherTest, RequestCredentialWithTLSErrors) {
+ // If we encounter TLS errors, we won't return credentials.
+ EXPECT_CALL(*client_, DidLastPageLoadEncounterSSLErrors())
+ .WillRepeatedly(testing::Return(true));
+
+ store_->AddLogin(form_);
+
+ std::vector<GURL> federations;
+ dispatcher()->OnRequestCredential(kRequestId, true, true, federations);
+
+ ExpectZeroClickSignInFailure();
+}
+
+TEST_F(CredentialManagerDispatcherTest,
+ CredentialManagerOnRequestCredentialWithZeroClickOnlyTwoPasswordStore) {
+ store_->AddLogin(form_);
+ store_->AddLogin(origin_path_form_);
+
+ std::vector<GURL> federations;
+ EXPECT_CALL(*client_, PromptUserToChooseCredentialsPtr(_, _, _, _))
+ .Times(testing::Exactly(0));
+ EXPECT_CALL(*client_, NotifyUserAutoSigninPtr(_)).Times(testing::Exactly(0));
+
+ dispatcher()->OnRequestCredential(kRequestId, true, true, federations);
+
+ RunAllPendingTasks();
+
+ const uint32_t kMsgID = CredentialManagerMsg_SendCredential::ID;
+ const IPC::Message* message =
+ process()->sink().GetFirstMessageMatching(kMsgID);
+ EXPECT_TRUE(message);
+ CredentialManagerMsg_SendCredential::Param send_param;
+ CredentialManagerMsg_SendCredential::Read(message, &send_param);
+
+ // With two items in the password store, we shouldn't get credentials back.
+ EXPECT_EQ(CredentialType::CREDENTIAL_TYPE_EMPTY,
+ std::get<1>(send_param).type);
+}
+
+TEST_F(CredentialManagerDispatcherTest,
+ OnRequestCredentialWithZeroClickOnlyAndSkipZeroClickPasswordStore) {
+ form_.skip_zero_click = true;
+ store_->AddLogin(form_);
+ store_->AddLogin(origin_path_form_);
+
+ std::vector<GURL> federations;
+ EXPECT_CALL(*client_, PromptUserToChooseCredentialsPtr(_, _, _, _))
+ .Times(testing::Exactly(0));
+ EXPECT_CALL(*client_, NotifyUserAutoSigninPtr(_)).Times(testing::Exactly(0));
+
+ dispatcher()->OnRequestCredential(kRequestId, true, true, federations);
+
+ RunAllPendingTasks();
+
+ const uint32_t kMsgID = CredentialManagerMsg_SendCredential::ID;
+ const IPC::Message* message =
+ process()->sink().GetFirstMessageMatching(kMsgID);
+ EXPECT_TRUE(message);
+ CredentialManagerMsg_SendCredential::Param send_param;
+ CredentialManagerMsg_SendCredential::Read(message, &send_param);
+
+ // With two items in the password store, we shouldn't get credentials back,
+ // even though only one item has |skip_zero_click| set |false|.
+ EXPECT_EQ(CredentialType::CREDENTIAL_TYPE_EMPTY,
+ std::get<1>(send_param).type);
+}
+
+TEST_F(CredentialManagerDispatcherTest,
+ OnRequestCredentialWithZeroClickOnlyCrossOriginPasswordStore) {
+ store_->AddLogin(cross_origin_form_);
+
+ form_.skip_zero_click = true;
+ store_->AddLogin(form_);
+
+ std::vector<GURL> federations;
+ EXPECT_CALL(*client_, PromptUserToChooseCredentialsPtr(_, _, _, _))
+ .Times(testing::Exactly(0));
+ EXPECT_CALL(*client_, NotifyUserAutoSigninPtr(_)).Times(testing::Exactly(0));
+
+ dispatcher()->OnRequestCredential(kRequestId, true, true, federations);
+
+ RunAllPendingTasks();
+
+ const uint32_t kMsgID = CredentialManagerMsg_SendCredential::ID;
+ const IPC::Message* message =
+ process()->sink().GetFirstMessageMatching(kMsgID);
+ EXPECT_TRUE(message);
+ CredentialManagerMsg_SendCredential::Param send_param;
+ CredentialManagerMsg_SendCredential::Read(message, &send_param);
+
+ // We only have cross-origin zero-click credentials; they should not be
+ // returned.
+ EXPECT_EQ(CredentialType::CREDENTIAL_TYPE_EMPTY,
+ std::get<1>(send_param).type);
+}
+
+TEST_F(CredentialManagerDispatcherTest,
+ CredentialManagerOnRequestCredentialWhileRequestPending) {
+ client_->set_zero_click_enabled(false);
+ store_->AddLogin(form_);
+
+ std::vector<GURL> federations;
+ EXPECT_CALL(*client_, PromptUserToChooseCredentialsPtr(_, _, _, _))
+ .Times(testing::Exactly(0));
+ EXPECT_CALL(*client_, NotifyUserAutoSigninPtr(_)).Times(testing::Exactly(0));
+
+ dispatcher()->OnRequestCredential(kRequestId, false, true, federations);
+ dispatcher()->OnRequestCredential(kRequestId + 1, false, true, federations);
+
+ // Check that the second request triggered a rejection.
+ uint32_t kMsgID = CredentialManagerMsg_RejectCredentialRequest::ID;
+ const IPC::Message* message =
+ process()->sink().GetFirstMessageMatching(kMsgID);
+ EXPECT_TRUE(message);
+
+ CredentialManagerMsg_RejectCredentialRequest::Param reject_param;
+ CredentialManagerMsg_RejectCredentialRequest::Read(message, &reject_param);
+ EXPECT_EQ(blink::WebCredentialManagerPendingRequestError,
+ std::get<1>(reject_param));
+ EXPECT_CALL(*client_, PromptUserToChooseCredentialsPtr(_, _, _, _))
+ .Times(testing::Exactly(1));
+ EXPECT_CALL(*client_, NotifyUserAutoSigninPtr(_)).Times(testing::Exactly(0));
+
+ process()->sink().ClearMessages();
+
+ // Execute the PasswordStore asynchronousness.
+ RunAllPendingTasks();
+
+ // Check that the first request resolves.
+ kMsgID = CredentialManagerMsg_SendCredential::ID;
+ message = process()->sink().GetFirstMessageMatching(kMsgID);
+ EXPECT_TRUE(message);
+ CredentialManagerMsg_SendCredential::Param send_param;
+ CredentialManagerMsg_SendCredential::Read(message, &send_param);
+ EXPECT_NE(CredentialType::CREDENTIAL_TYPE_EMPTY,
+ std::get<1>(send_param).type);
+ process()->sink().ClearMessages();
+}
+
+TEST_F(CredentialManagerDispatcherTest, ResetSkipZeroClickAfterPrompt) {
+ // Turn on the global zero-click flag, and add two credentials in separate
+ // origins, both set to skip zero-click.
+ client_->set_zero_click_enabled(true);
+ form_.skip_zero_click = true;
+ store_->AddLogin(form_);
+ cross_origin_form_.skip_zero_click = true;
+ store_->AddLogin(cross_origin_form_);
+
+ // Execute the PasswordStore asynchronousness to ensure everything is
+ // written before proceeding.
+ RunAllPendingTasks();
+
+ // Sanity check.
+ TestPasswordStore::PasswordMap passwords = store_->stored_passwords();
+ EXPECT_EQ(2U, passwords.size());
+ EXPECT_EQ(1U, passwords[form_.signon_realm].size());
+ EXPECT_EQ(1U, passwords[cross_origin_form_.signon_realm].size());
+ EXPECT_TRUE(passwords[form_.signon_realm][0].skip_zero_click);
+ EXPECT_TRUE(passwords[cross_origin_form_.signon_realm][0].skip_zero_click);
+
+ // Trigger a request which should return the credential found in |form_|, and
+ // wait for it to process.
+ std::vector<GURL> federations;
+ // Check that the form in the database has been updated. `OnRequestCredential`
+ // generates a call to prompt the user to choose a credential.
+ // MockPasswordManagerClient mocks a user choice, and when users choose a
+ // credential (and have the global zero-click flag enabled), we make sure that
+ // they'll be logged in again next time.
+ EXPECT_CALL(*client_, PromptUserToChooseCredentialsPtr(_, _, _, _))
+ .Times(testing::Exactly(1));
+ EXPECT_CALL(*client_, NotifyUserAutoSigninPtr(_)).Times(testing::Exactly(0));
+
+ dispatcher()->OnRequestCredential(kRequestId, false, true, federations);
+ RunAllPendingTasks();
+
+ passwords = store_->stored_passwords();
+ EXPECT_EQ(2U, passwords.size());
+ EXPECT_EQ(1U, passwords[form_.signon_realm].size());
+ EXPECT_EQ(1U, passwords[cross_origin_form_.signon_realm].size());
+ EXPECT_FALSE(passwords[form_.signon_realm][0].skip_zero_click);
+ EXPECT_TRUE(passwords[cross_origin_form_.signon_realm][0].skip_zero_click);
+}
+
+TEST_F(CredentialManagerDispatcherTest,
+ NoResetSkipZeroClickAfterPromptInIncognito) {
+ EXPECT_CALL(*client_, IsOffTheRecord()).WillRepeatedly(testing::Return(true));
+ // Turn on the global zero-click flag which should be overriden by Incognito.
+ client_->set_zero_click_enabled(true);
+ form_.skip_zero_click = true;
+ store_->AddLogin(form_);
+ RunAllPendingTasks();
+
+ // Sanity check.
+ TestPasswordStore::PasswordMap passwords = store_->stored_passwords();
+ ASSERT_EQ(1U, passwords.size());
+ ASSERT_EQ(1U, passwords[form_.signon_realm].size());
+ EXPECT_TRUE(passwords[form_.signon_realm][0].skip_zero_click);
+
+ // Trigger a request which should return the credential found in |form_|, and
+ // wait for it to process.
+ EXPECT_CALL(*client_, PromptUserToChooseCredentialsPtr(_, _, _, _))
+ .Times(testing::Exactly(1));
+ EXPECT_CALL(*client_, NotifyUserAutoSigninPtr(_)).Times(testing::Exactly(0));
+
+ dispatcher()->OnRequestCredential(kRequestId, false, true,
+ std::vector<GURL>());
+ RunAllPendingTasks();
+
+ // The form shouldn't become a zero-click one.
+ passwords = store_->stored_passwords();
+ ASSERT_EQ(1U, passwords.size());
+ ASSERT_EQ(1U, passwords[form_.signon_realm].size());
+ EXPECT_TRUE(passwords[form_.signon_realm][0].skip_zero_click);
+}
+
+TEST_F(CredentialManagerDispatcherTest, IncognitoZeroClickRequestCredential) {
+ EXPECT_CALL(*client_, IsOffTheRecord()).WillRepeatedly(testing::Return(true));
+ store_->AddLogin(form_);
+
+ std::vector<GURL> federations;
+ EXPECT_CALL(*client_, PromptUserToChooseCredentialsPtr(_, _, _, _))
+ .Times(testing::Exactly(0));
+ EXPECT_CALL(*client_, NotifyUserAutoSigninPtr(_)).Times(testing::Exactly(0));
+
+ dispatcher()->OnRequestCredential(kRequestId, true, true, federations);
+
+ RunAllPendingTasks();
+
+ const uint32_t kMsgID = CredentialManagerMsg_SendCredential::ID;
+ const IPC::Message* message =
+ process()->sink().GetFirstMessageMatching(kMsgID);
+ ASSERT_TRUE(message);
+ CredentialManagerMsg_SendCredential::Param param;
+ CredentialManagerMsg_SendCredential::Read(message, &param);
+ EXPECT_EQ(CredentialType::CREDENTIAL_TYPE_EMPTY, std::get<1>(param).type);
+}
+
+TEST_F(CredentialManagerDispatcherTest,
+ ZeroClickWithAffiliatedFormInPasswordStore) {
+ // Insert the affiliated form into the store, and mock out the association
+ // with the current origin. As it's the only form matching the origin, it
+ // ought to be returned automagically.
+ store_->AddLogin(affiliated_form1_);
+
+ auto mock_helper = base::WrapUnique(new MockAffiliatedMatchHelper);
+ store_->SetAffiliatedMatchHelper(std::move(mock_helper));
+
+ std::vector<GURL> federations;
+ std::vector<std::string> affiliated_realms;
+ affiliated_realms.push_back(kTestAndroidRealm1);
+ static_cast<MockAffiliatedMatchHelper*>(store_->affiliated_match_helper())
+ ->ExpectCallToGetAffiliatedAndroidRealms(
+ dispatcher_->GetSynthesizedFormForOrigin(), affiliated_realms);
+
+ dispatcher()->OnRequestCredential(kRequestId, true, true, federations);
+
+ ExpectZeroClickSignInSuccess(CredentialType::CREDENTIAL_TYPE_PASSWORD);
+}
+
+TEST_F(CredentialManagerDispatcherTest,
+ ZeroClickWithTwoAffiliatedFormsInPasswordStore) {
+ // Insert two affiliated forms into the store, and mock out the association
+ // with the current origin. Multiple forms === no zero-click sign in.
+ store_->AddLogin(affiliated_form1_);
+ store_->AddLogin(affiliated_form2_);
+
+ auto mock_helper = base::WrapUnique(new MockAffiliatedMatchHelper);
+ store_->SetAffiliatedMatchHelper(std::move(mock_helper));
+
+ std::vector<GURL> federations;
+ std::vector<std::string> affiliated_realms;
+ affiliated_realms.push_back(kTestAndroidRealm1);
+ affiliated_realms.push_back(kTestAndroidRealm2);
+ static_cast<MockAffiliatedMatchHelper*>(store_->affiliated_match_helper())
+ ->ExpectCallToGetAffiliatedAndroidRealms(
+ dispatcher_->GetSynthesizedFormForOrigin(), affiliated_realms);
+
+ dispatcher()->OnRequestCredential(kRequestId, true, true, federations);
+
+ ExpectZeroClickSignInFailure();
+}
+
+TEST_F(CredentialManagerDispatcherTest,
+ ZeroClickWithUnaffiliatedFormsInPasswordStore) {
+ // Insert the affiliated form into the store, but don't mock out the
+ // association with the current origin. No association === no zero-click sign
+ // in.
+ store_->AddLogin(affiliated_form1_);
+
+ auto mock_helper = base::WrapUnique(new MockAffiliatedMatchHelper);
+ store_->SetAffiliatedMatchHelper(std::move(mock_helper));
+
+ std::vector<GURL> federations;
+ std::vector<std::string> affiliated_realms;
+ static_cast<MockAffiliatedMatchHelper*>(store_->affiliated_match_helper())
+ ->ExpectCallToGetAffiliatedAndroidRealms(
+ dispatcher_->GetSynthesizedFormForOrigin(), affiliated_realms);
+
+ dispatcher()->OnRequestCredential(kRequestId, true, true, federations);
+
+ ExpectZeroClickSignInFailure();
+}
+
+TEST_F(CredentialManagerDispatcherTest,
+ ZeroClickWithFormAndUnaffiliatedFormsInPasswordStore) {
+ // Insert the affiliated form into the store, along with a real form for the
+ // origin, and don't mock out the association with the current origin. No
+ // association + existing form === zero-click sign in.
+ store_->AddLogin(form_);
+ store_->AddLogin(affiliated_form1_);
+
+ auto mock_helper = base::WrapUnique(new MockAffiliatedMatchHelper);
+ store_->SetAffiliatedMatchHelper(std::move(mock_helper));
+
+ std::vector<GURL> federations;
+ std::vector<std::string> affiliated_realms;
+ static_cast<MockAffiliatedMatchHelper*>(store_->affiliated_match_helper())
+ ->ExpectCallToGetAffiliatedAndroidRealms(
+ dispatcher_->GetSynthesizedFormForOrigin(), affiliated_realms);
+
+ dispatcher()->OnRequestCredential(kRequestId, true, true, federations);
+
+ ExpectZeroClickSignInSuccess(CredentialType::CREDENTIAL_TYPE_PASSWORD);
+}
+
+TEST_F(CredentialManagerDispatcherTest, GetSynthesizedFormForOrigin) {
+ autofill::PasswordForm synthesized =
+ dispatcher_->GetSynthesizedFormForOrigin();
+ EXPECT_EQ(kTestWebOrigin, synthesized.origin.spec());
+ EXPECT_EQ(kTestWebOrigin, synthesized.signon_realm);
+ EXPECT_EQ(autofill::PasswordForm::SCHEME_HTML, synthesized.scheme);
+ EXPECT_TRUE(synthesized.ssl_valid);
+}
+
+} // namespace password_manager

Powered by Google App Engine
This is Rietveld 408576698