text: // 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 "chrome/browser/password_manager/chrome_password_manager_client.h"
  
  #include "base/command_line.h"
  #include "base/strings/string16.h"
  #include "base/strings/utf_string_conversions.h"
  #include "chrome/browser/sync/profile_sync_service_factory.h"
  #include "chrome/browser/sync/profile_sync_service_mock.h"
  #include "chrome/common/chrome_version_info.h"
  #include "chrome/test/base/chrome_render_view_host_test_harness.h"
  #include "chrome/test/base/testing_profile.h"
  #include "components/autofill/content/common/autofill_messages.h"
  #include "components/password_manager/content/browser/password_manager_internals_service_factory.h"
  #include "components/password_manager/content/common/credential_manager_messages.h"
  #include "components/password_manager/content/common/credential_manager_types.h"
  #include "components/password_manager/core/browser/log_receiver.h"
  #include "components/password_manager/core/browser/password_manager_internals_service.h"
  #include "components/password_manager/core/common/password_manager_switches.h"
  #include "content/public/browser/browser_context.h"
  #include "content/public/browser/web_contents.h"
  #include "content/public/test/mock_render_process_host.h"
  #include "testing/gmock/include/gmock/gmock.h"
  #include "testing/gtest/include/gtest/gtest.h"
  
  using content::BrowserContext;
  using content::WebContents;
  using testing::Return;
  
  namespace {
  
  const char kTestText[] = "abcd1234";
  
  class MockLogReceiver : public password_manager::LogReceiver {
   public:
    MOCK_METHOD1(LogSavePasswordProgress, void(const std::string&));
  };
  
  class TestChromePasswordManagerClient : public ChromePasswordManagerClient {
   public:
    explicit TestChromePasswordManagerClient(content::WebContents* web_contents)
        : ChromePasswordManagerClient(web_contents, NULL),
          is_sync_account_credential_(false) {}
    ~TestChromePasswordManagerClient() override {}
  
    bool IsSyncAccountCredential(const std::string& username,
                                 const std::string& origin) const override {
      return is_sync_account_credential_;
    }
  
    void set_is_sync_account_credential(bool is_sync_account_credential) {
      is_sync_account_credential_ = is_sync_account_credential;
    }
  
   private:
    bool is_sync_account_credential_;
  
    DISALLOW_COPY_AND_ASSIGN(TestChromePasswordManagerClient);
  };
  
  }  // namespace
  
  class ChromePasswordManagerClientTest : public ChromeRenderViewHostTestHarness {
   public:
    ChromePasswordManagerClientTest();
  
    virtual void SetUp() override;
  
   protected:
    ChromePasswordManagerClient* GetClient();
  
    // If the test IPC sink contains an AutofillMsg_SetLoggingState message, then
    // copies its argument into |activation_flag| and returns true. Otherwise
    // returns false.
    bool WasLoggingActivationMessageSent(bool* activation_flag);
  
    password_manager::PasswordManagerInternalsService* service_;
  
    testing::StrictMock<MockLogReceiver> receiver_;
  };
  
  ChromePasswordManagerClientTest::ChromePasswordManagerClientTest()
      : service_(NULL) {
  }
  
  void ChromePasswordManagerClientTest::SetUp() {
    ChromeRenderViewHostTestHarness::SetUp();
    ChromePasswordManagerClient::CreateForWebContentsWithAutofillClient(
        web_contents(), NULL);
    service_ = password_manager::PasswordManagerInternalsServiceFactory::
        GetForBrowserContext(profile());
    ASSERT_TRUE(service_);
  }
  
  ChromePasswordManagerClient* ChromePasswordManagerClientTest::GetClient() {
    return ChromePasswordManagerClient::FromWebContents(web_contents());
  }
  
  bool ChromePasswordManagerClientTest::WasLoggingActivationMessageSent(
      bool* activation_flag) {
    const uint32 kMsgID = AutofillMsg_SetLoggingState::ID;
    const IPC::Message* message =
        process()->sink().GetFirstMessageMatching(kMsgID);
    if (!message)
      return false;
    Tuple<bool> param;
    AutofillMsg_SetLoggingState::Read(message, &param);
    *activation_flag = get<0>(param);
    process()->sink().ClearMessages();
    return true;
  }
  
  TEST_F(ChromePasswordManagerClientTest, LogSavePasswordProgressNoReceiver) {
    ChromePasswordManagerClient* client = GetClient();
  
    EXPECT_CALL(receiver_, LogSavePasswordProgress(kTestText)).Times(0);
    // Before attaching the receiver, no text should be passed.
    client->LogSavePasswordProgress(kTestText);
    EXPECT_FALSE(client->IsLoggingActive());
  }
  
  TEST_F(ChromePasswordManagerClientTest, LogSavePasswordProgressAttachReceiver) {
    ChromePasswordManagerClient* client = GetClient();
    EXPECT_FALSE(client->IsLoggingActive());
  
    // After attaching the logger, text should be passed.
    service_->RegisterReceiver(&receiver_);
    EXPECT_TRUE(client->IsLoggingActive());
    EXPECT_CALL(receiver_, LogSavePasswordProgress(kTestText)).Times(1);
    client->LogSavePasswordProgress(kTestText);
    service_->UnregisterReceiver(&receiver_);
    EXPECT_FALSE(client->IsLoggingActive());
  }
  
  TEST_F(ChromePasswordManagerClientTest, LogSavePasswordProgressDetachReceiver) {
    ChromePasswordManagerClient* client = GetClient();
  
    service_->RegisterReceiver(&receiver_);
    EXPECT_TRUE(client->IsLoggingActive());
    service_->UnregisterReceiver(&receiver_);
    EXPECT_FALSE(client->IsLoggingActive());
  
    // After detaching the logger, no text should be passed.
    EXPECT_CALL(receiver_, LogSavePasswordProgress(kTestText)).Times(0);
    client->LogSavePasswordProgress(kTestText);
  }
  
  TEST_F(ChromePasswordManagerClientTest, LogSavePasswordProgressNotifyRenderer) {
    ChromePasswordManagerClient* client = GetClient();
    bool logging_active = false;
  
    // Initially, the logging should be off, so no IPC messages.
    EXPECT_FALSE(WasLoggingActivationMessageSent(&logging_active));
  
    service_->RegisterReceiver(&receiver_);
    EXPECT_TRUE(client->IsLoggingActive());
    EXPECT_TRUE(WasLoggingActivationMessageSent(&logging_active));
    EXPECT_TRUE(logging_active);
  
    service_->UnregisterReceiver(&receiver_);
    EXPECT_FALSE(client->IsLoggingActive());
    EXPECT_TRUE(WasLoggingActivationMessageSent(&logging_active));
    EXPECT_FALSE(logging_active);
  }
  
  TEST_F(ChromePasswordManagerClientTest, AnswerToPingsAboutLoggingState_Active) {
    service_->RegisterReceiver(&receiver_);
  
    process()->sink().ClearMessages();
  
    // Ping the client for logging activity update.
    AutofillHostMsg_PasswordAutofillAgentConstructed msg(0);
    static_cast<content::WebContentsObserver*>(GetClient())->OnMessageReceived(
        msg, web_contents()->GetMainFrame());
  
    bool logging_active = false;
    EXPECT_TRUE(WasLoggingActivationMessageSent(&logging_active));
    EXPECT_TRUE(logging_active);
  
    service_->UnregisterReceiver(&receiver_);
  }
  
  TEST_F(ChromePasswordManagerClientTest,
         AnswerToPingsAboutLoggingState_Inactive) {
    process()->sink().ClearMessages();
  
    // Ping the client for logging activity update.
    AutofillHostMsg_PasswordAutofillAgentConstructed msg(0);
    static_cast<content::WebContentsObserver*>(GetClient())->OnMessageReceived(
        msg, web_contents()->GetMainFrame());
  
    bool logging_active = true;
    EXPECT_TRUE(WasLoggingActivationMessageSent(&logging_active));
    EXPECT_FALSE(logging_active);
  }
  
  TEST_F(ChromePasswordManagerClientTest,
         IsAutomaticPasswordSavingEnabledDefaultBehaviourTest) {
    EXPECT_FALSE(GetClient()->IsAutomaticPasswordSavingEnabled());
  }
  
  TEST_F(ChromePasswordManagerClientTest,
         IsAutomaticPasswordSavingEnabledWhenFlagIsSetTest) {
    base::CommandLine::ForCurrentProcess()->AppendSwitch(
        password_manager::switches::kEnableAutomaticPasswordSaving);
    if (chrome::VersionInfo::GetChannel() == chrome::VersionInfo::CHANNEL_UNKNOWN)
      EXPECT_TRUE(GetClient()->IsAutomaticPasswordSavingEnabled());
    else
      EXPECT_FALSE(GetClient()->IsAutomaticPasswordSavingEnabled());
  }
  
  TEST_F(ChromePasswordManagerClientTest, LogToAReceiver) {
    ChromePasswordManagerClient* client = GetClient();
    service_->RegisterReceiver(&receiver_);
    EXPECT_TRUE(client->IsLoggingActive());
  
    EXPECT_CALL(receiver_, LogSavePasswordProgress(kTestText)).Times(1);
    client->LogSavePasswordProgress(kTestText);
  
    service_->UnregisterReceiver(&receiver_);
    EXPECT_FALSE(client->IsLoggingActive());
  }
  
  TEST_F(ChromePasswordManagerClientTest,
         ShouldAskUserToSubmitURLDefaultBehaviour) {
    ChromePasswordManagerClient* client = GetClient();
    // TODO(melandory) Since "Ask user to submit URL" functionality is currently
    // in development, so the user should not be asked to submit a URL.
    EXPECT_FALSE(client->ShouldAskUserToSubmitURL(GURL("https://hostname.com/")));
  }
  
  TEST_F(ChromePasswordManagerClientTest, ShouldAskUserToSubmitURLEmptyURL) {
    ChromePasswordManagerClient* client = GetClient();
    EXPECT_FALSE(client->ShouldAskUserToSubmitURL(GURL::EmptyGURL()));
  }
  
  TEST_F(ChromePasswordManagerClientTest, ShouldFilterAutofillResult_Reauth) {
    // Make client disallow only reauth requests.
    base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
    command_line->AppendSwitch(
        password_manager::switches::kDisallowAutofillSyncCredentialForReauth);
    scoped_ptr<TestChromePasswordManagerClient> client(
        new TestChromePasswordManagerClient(web_contents()));
    autofill::PasswordForm form;
  
    client->set_is_sync_account_credential(false);
    NavigateAndCommit(
        GURL("https://accounts.google.com/login?rart=123&continue=blah"));
    EXPECT_FALSE(client->ShouldFilterAutofillResult(form));
  
    client->set_is_sync_account_credential(true);
    NavigateAndCommit(
        GURL("https://accounts.google.com/login?rart=123&continue=blah"));
    EXPECT_TRUE(client->ShouldFilterAutofillResult(form));
  
    // This counts as a reauth url, though a valid URL should have a value for
    // "rart"
    NavigateAndCommit(GURL("https://accounts.google.com/addlogin?rart"));
    EXPECT_TRUE(client->ShouldFilterAutofillResult(form));
  
    NavigateAndCommit(GURL("https://accounts.google.com/login?param=123"));
    EXPECT_FALSE(client->ShouldFilterAutofillResult(form));
  
    NavigateAndCommit(GURL("https://site.com/login?rart=678"));
    EXPECT_FALSE(client->ShouldFilterAutofillResult(form));
  }
  
  TEST_F(ChromePasswordManagerClientTest, ShouldFilterAutofillResult) {
    // Normally the client should allow any credentials through, even if they
    // are the sync credential.
    scoped_ptr<TestChromePasswordManagerClient> client(
        new TestChromePasswordManagerClient(web_contents()));
    autofill::PasswordForm form;
    client->set_is_sync_account_credential(true);
    NavigateAndCommit(GURL("https://accounts.google.com/Login"));
    EXPECT_FALSE(client->ShouldFilterAutofillResult(form));
  
    // Adding disallow switch should cause sync credential to be filtered.
    base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
    command_line->AppendSwitch(
        password_manager::switches::kDisallowAutofillSyncCredential);
    client.reset(new TestChromePasswordManagerClient(web_contents()));
    client->set_is_sync_account_credential(true);
    NavigateAndCommit(GURL("https://accounts.google.com/Login"));
    EXPECT_TRUE(client->ShouldFilterAutofillResult(form));
  }
  
  TEST_F(ChromePasswordManagerClientTest,
         IsPasswordManagerEnabledForCurrentPage) {
    ChromePasswordManagerClient* client = GetClient();
    NavigateAndCommit(
        GURL("https://accounts.google.com/ServiceLogin?continue="
             "https://passwords.google.com/settings&rart=123"));
    EXPECT_FALSE(client->IsPasswordManagerEnabledForCurrentPage());
  
    // Password site is inaccesible via HTTP, but because of HSTS the following
    // link should still continue to https://passwords.google.com.
    NavigateAndCommit(
        GURL("https://accounts.google.com/ServiceLogin?continue="
             "http://passwords.google.com/settings&rart=123"));
    EXPECT_FALSE(client->IsPasswordManagerEnabledForCurrentPage());
  
    // Specifying default port still passes.
    NavigateAndCommit(
        GURL("https://accounts.google.com/ServiceLogin?continue="
             "https://passwords.google.com:443/settings&rart=123"));
    EXPECT_FALSE(client->IsPasswordManagerEnabledForCurrentPage());
  
    // Encoded URL is considered the same.
    NavigateAndCommit(
        GURL("https://accounts.google.com/ServiceLogin?continue="
             "https://passwords.%67oogle.com/settings&rart=123"));
    EXPECT_FALSE(client->IsPasswordManagerEnabledForCurrentPage());
  
    // Make sure testing sites are disabled as well.
    NavigateAndCommit(
        GURL("https://accounts.google.com/Login?continue="
             "https://passwords-ac-testing.corp.google.com/settings&rart=456"));
    EXPECT_FALSE(client->IsPasswordManagerEnabledForCurrentPage());
  
    // Fully qualified domain name is considered a different hostname by GURL.
    // Ideally this would not be the case, but this quirk can be avoided by
    // verification on the server. This test is simply documentation of this
    // behavior.
    NavigateAndCommit(
        GURL("https://accounts.google.com/ServiceLogin?continue="
             "https://passwords.google.com./settings&rart=123"));
    EXPECT_TRUE(client->IsPasswordManagerEnabledForCurrentPage());
  
    // Not a transactional reauth page.
    NavigateAndCommit(
        GURL("https://accounts.google.com/ServiceLogin?continue="
             "https://passwords.google.com/settings"));
    EXPECT_TRUE(client->IsPasswordManagerEnabledForCurrentPage());
  
    // Should be enabled for other transactional reauth pages.
    NavigateAndCommit(
        GURL("https://accounts.google.com/ServiceLogin?continue="
             "https://mail.google.com&rart=234"));
    EXPECT_TRUE(client->IsPasswordManagerEnabledForCurrentPage());
  
    // Reauth pages are only on accounts.google.com
    NavigateAndCommit(
        GURL("https://other.site.com/ServiceLogin?continue="
             "https://passwords.google.com&rart=234"));
    EXPECT_TRUE(client->IsPasswordManagerEnabledForCurrentPage());
  }
  
  TEST_F(ChromePasswordManagerClientTest, IsPasswordSyncEnabled) {
    ChromePasswordManagerClient* client = GetClient();
  
    ProfileSyncServiceMock* mock_sync_service =
        static_cast<ProfileSyncServiceMock*>(
            ProfileSyncServiceFactory::GetInstance()->SetTestingFactoryAndUse(
                profile(), ProfileSyncServiceMock::BuildMockProfileSyncService));
  
    syncer::ModelTypeSet active_types;
    active_types.Put(syncer::PASSWORDS);
    EXPECT_CALL(*mock_sync_service, HasSyncSetupCompleted())
        .WillRepeatedly(Return(true));
    EXPECT_CALL(*mock_sync_service, SyncActive()).WillRepeatedly(Return(true));
    EXPECT_CALL(*mock_sync_service, GetActiveDataTypes())
        .WillRepeatedly(Return(active_types));
    EXPECT_CALL(*mock_sync_service, IsUsingSecondaryPassphrase())
        .WillRepeatedly(Return(false));
  
    // Passwords are syncing and custom passphrase isn't used.
    EXPECT_FALSE(
        client->IsPasswordSyncEnabled(password_manager::ONLY_CUSTOM_PASSPHRASE));
    EXPECT_TRUE(client->IsPasswordSyncEnabled(
        password_manager::WITHOUT_CUSTOM_PASSPHRASE));
  
    // Again, using a custom passphrase.
    EXPECT_CALL(*mock_sync_service, IsUsingSecondaryPassphrase())
        .WillRepeatedly(Return(true));
  
    EXPECT_TRUE(
        client->IsPasswordSyncEnabled(password_manager::ONLY_CUSTOM_PASSPHRASE));
    EXPECT_FALSE(client->IsPasswordSyncEnabled(
        password_manager::WITHOUT_CUSTOM_PASSPHRASE));
  
    // Always return false if we aren't syncing passwords.
    active_types.Remove(syncer::PASSWORDS);
    active_types.Put(syncer::BOOKMARKS);
    EXPECT_CALL(*mock_sync_service, GetActiveDataTypes())
        .WillRepeatedly(Return(active_types));
  
    EXPECT_FALSE(
        client->IsPasswordSyncEnabled(password_manager::ONLY_CUSTOM_PASSPHRASE));
    EXPECT_FALSE(client->IsPasswordSyncEnabled(
        password_manager::WITHOUT_CUSTOM_PASSPHRASE));
  
    // Again, without a custom passphrase.
    EXPECT_CALL(*mock_sync_service, IsUsingSecondaryPassphrase())
        .WillRepeatedly(Return(false));
  
    EXPECT_FALSE(
        client->IsPasswordSyncEnabled(password_manager::ONLY_CUSTOM_PASSPHRASE));
    EXPECT_FALSE(client->IsPasswordSyncEnabled(
        password_manager::WITHOUT_CUSTOM_PASSPHRASE));
  }
  