chromium/chrome/browser/profiles/profile_browsertest.cc

// Copyright 2012 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/profiles/profile.h"

#include <stddef.h>

#include <memory>

#include "base/check.h"
#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/json/json_reader.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/no_destructor.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/scoped_multi_source_observation.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_restrictions.h"
#include "base/values.h"
#include "base/version.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/browser_features.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/chrome_version_service.h"
#include "chrome/browser/profiles/keep_alive/profile_keep_alive_types.h"
#include "chrome/browser/profiles/keep_alive/scoped_profile_keep_alive.h"
#include "chrome/browser/profiles/profile_destroyer.h"
#include "chrome/browser/profiles/profile_impl.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/profiles/profile_observer.h"
#include "chrome/browser/profiles/profile_test_util.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/test/test_browser_closed_waiter.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/profile_destruction_waiter.h"
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/keep_alive_registry/keep_alive_types.h"
#include "components/keep_alive_registry/scoped_keep_alive.h"
#include "components/prefs/pref_service.h"
#include "components/profile_metrics/browser_profile_type.h"
#include "components/version_info/version_info.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/test_utils.h"
#include "extensions/buildflags/buildflags.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
#include "net/dns/mock_host_resolver.h"
#include "net/net_buildflags.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/url_request/url_request_failed_job.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
#include "url/url_constants.h"

#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "ash/constants/ash_switches.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#endif

#if BUILDFLAG(ENABLE_EXTENSIONS)
#include "extensions/browser/extension_protocols.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_builder.h"
#endif

#if BUILDFLAG(IS_CHROMEOS_LACROS)
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/profiles/profile_attributes_entry.h"
#include "chrome/browser/profiles/profile_attributes_storage.h"
#include "chromeos/crosapi/mojom/crosapi.mojom.h"
#include "chromeos/startup/browser_init_params.h"
#include "chromeos/startup/browser_params_proxy.h"
#include "components/account_id/account_id.h"
#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)

#if BUILDFLAG(ENABLE_SESSION_SERVICE)
#include "chrome/browser/sessions/exit_type_service.h"
#endif

namespace {

// A helper class which creates a SimpleURLLoader with an expected final status
// and the ability to wait until a request completes. It's not considered a
// failure for the load to never complete.
class SimpleURLLoaderHelper {};

class MockProfileDelegate : public Profile::Delegate {};

// Creates a prefs file in the given directory.
void CreatePrefsFileInDirectory(const base::FilePath& directory_path) {}

void CheckChromeVersion(Profile* profile, bool is_new) {}

void FlushTaskRunner(base::SequencedTaskRunner* runner) {}

void SpinThreads() {}

}  // namespace

class ProfileBrowserTest : public InProcessBrowserTest {};

// Test OnProfileCreate is called with is_new_profile set to true when
// creating a new profile synchronously.
// TODO(crbug.com/40771709): Flaky on ChromeOS-Ash.
// TODO(crbug.com/40826385): Failing on Mac.
#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_MAC)
#define MAYBE_CreateNewProfileSynchronous
#else
#define MAYBE_CreateNewProfileSynchronous
#endif
IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, MAYBE_CreateNewProfileSynchronous) {}

// Test OnProfileCreate is called with is_new_profile set to false when
// creating a profile synchronously with an existing prefs file.
// TODO(crbug.com/40826385): Failing on Mac.
// TODO(b/328177667): Flaky on linux-chromeos-rel.
#if BUILDFLAG(IS_MAC) || (BUILDFLAG(IS_CHROMEOS) && defined(NDEBUG))
#define MAYBE_CreateOldProfileSynchronous
#else
#define MAYBE_CreateOldProfileSynchronous
#endif
IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, MAYBE_CreateOldProfileSynchronous) {}

// Test OnProfileCreate is called with is_new_profile set to true when
// creating a new profile asynchronously.
// TODO(crbug.com/40811337): Flaky on ChromeOS-Ash.
// TODO(crbug.com/40826385): Failing on Mac.
#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_MAC)
#define MAYBE_CreateNewProfileAsynchronous
#else
#define MAYBE_CreateNewProfileAsynchronous
#endif
IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, MAYBE_CreateNewProfileAsynchronous) {}

// TODO(crbug.com/40812649): Flaky on ChromeOS-Ash.
// TODO(crbug.com/40771709): Flaky on Mac.
#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_MAC)
#define MAYBE_CreateOldProfileAsynchronous
#else
#define MAYBE_CreateOldProfileAsynchronous
#endif
// Test OnProfileCreate is called with is_new_profile set to false when
// creating a profile asynchronously with an existing prefs file.
IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, MAYBE_CreateOldProfileAsynchronous) {}

// Test that a README file is created for profiles that didn't have it.
// TODO(crbug.com/40817682): Flaky on ChromeOS-Ash.
// TODO(crbug.com/40826385): Failing on Mac.
#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_MAC)
#define MAYBE_ProfileReadmeCreated
#else
#define MAYBE_ProfileReadmeCreated
#endif
IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, MAYBE_ProfileReadmeCreated) {}

// The EndSession IO synchronization is only critical on Windows, but also
// happens under Ozone. See BrowserProcessImpl::EndSession.
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_OZONE)

namespace {

std::string GetExitTypePreferenceFromDisk(Profile* profile) {}

}  // namespace

IN_PROC_BROWSER_TEST_F(ProfileBrowserTest,
                       WritesProfilesSynchronouslyOnEndSession) {}

#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_OZONE)

// The following tests make sure that it's safe to shut down while one of the
// Profile's URLLoaderFactories is in use by a SimpleURLLoader.
IN_PROC_BROWSER_TEST_F(ProfileBrowserTest,
                       SimpleURLLoaderUsingMainContextDuringShutdown) {}

// The following tests make sure that it's safe to destroy an incognito profile
// while one of the its URLLoaderFactory is in use by a SimpleURLLoader.
IN_PROC_BROWSER_TEST_F(ProfileBrowserTest,
                       SimpleURLLoaderUsingMainContextDuringIncognitoTeardown) {}

#if BUILDFLAG(ENABLE_EXTENSIONS)
// Regression test for https://crbug.com/1136214 - verification that
// ExtensionURLLoaderFactory won't hit a use-after-free bug when used after
// a Profile has been torn down already.
IN_PROC_BROWSER_TEST_F(ProfileBrowserTest,
                       ExtensionURLLoaderFactoryAfterIncognitoTeardown) {}
#endif

// Verifies the cache directory supports multiple profiles when it's overridden
// by group policy or command line switches.
IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, DiskCacheDirOverride) {}

// Verifies the last selected directory has a default value.
IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, LastSelectedDirectory) {}

// Verifies creating an OTR with non-primary id results in a different profile
// from incognito profile.
IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, CreateNonPrimaryOTR) {}

// Verifies creating two OTRs with different ids results in different profiles.
IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, CreateTwoNonPrimaryOTRs) {}

class ProfileBrowserTestWithoutDestroyProfile : public ProfileBrowserTest {};

// Verifies destroying regular profile will result in destruction of OTR
// profiles.
// TODO(crbug.com/40924925): Re-enable this test on ChromeOS.
#if BUILDFLAG(IS_CHROMEOS)
#define MAYBE_DestroyRegularProfileBeforeOTRs
#else
#define MAYBE_DestroyRegularProfileBeforeOTRs
#endif
IN_PROC_BROWSER_TEST_F(ProfileBrowserTestWithoutDestroyProfile,
                       MAYBE_DestroyRegularProfileBeforeOTRs) {}

// Regression test for: https://crbug.com/1357476
IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, DestroyOnOTRProfileAmongMany) {}

#if !BUILDFLAG(IS_CHROMEOS_ASH)
class ProfileBrowserTestWithDestroyProfile : public ProfileBrowserTest {};

// Main profile is not yet destroyed on Lacros, test below to test destroying
// secondary profiles  on Lacros`LacrosSecondaryProfilesDestroyOnBrowserClose`.
#if !BUILDFLAG(IS_CHROMEOS_LACROS)
// Verifies the regular Profile doesn't get destroyed as long as there's an OTR
// Profile around.
IN_PROC_BROWSER_TEST_F(ProfileBrowserTestWithDestroyProfile,
                       OTRProfileKeepsRegularProfileAlive) {}
#endif  // !BUILDFLAG(IS_CHROMEOS_LACROS)

#if BUILDFLAG(IS_CHROMEOS_LACROS)
//  Test secondary profiles deleted on browser close on lacros.
//  Main profile remains alive.
IN_PROC_BROWSER_TEST_F(ProfileBrowserTestWithDestroyProfile,
                       LacrosSecondaryProfilesDestroyOnBrowserClose) {
  Profile* main_profile = browser()->profile();
  ASSERT_TRUE(Profile::IsMainProfilePath(main_profile->GetPath()));

  ProfileManager* profile_manager = g_browser_process->profile_manager();
  // Create a secondary profile.
  Profile& secondary_profile = profiles::testing::CreateProfileSync(
      profile_manager, profile_manager->GenerateNextProfileDirectoryPath());
  ASSERT_FALSE(Profile::IsMainProfilePath(secondary_profile.GetPath()));

  // Creates a browser for the secondary profile.
  Browser* secondary_browser = CreateBrowser(&secondary_profile);
  Browser* main_browser = browser();

  EXPECT_TRUE(profile_manager->HasKeepAliveForTesting(
      main_profile, ProfileKeepAliveOrigin::kLacrosMainProfile));
  EXPECT_FALSE(profile_manager->HasKeepAliveForTesting(
      &secondary_profile, ProfileKeepAliveOrigin::kLacrosMainProfile));

  // Destruction Waiters for both profiles.
  ProfileDestructionWaiter main_waiter(main_profile);
  ProfileDestructionWaiter secondary_waiter(&secondary_profile);

  // Close both browsers.
  CloseBrowserSynchronously(secondary_browser);
  CloseBrowserSynchronously(main_browser);
  base::RunLoop().RunUntilIdle();

  // Main profile has no more active browsers.
  EXPECT_FALSE(profile_manager->HasKeepAliveForTesting(
      main_profile, ProfileKeepAliveOrigin::kBrowserWindow));
  // But still has the `ProfileKeepAliveOrigin::kLacrosMainProfile` KeepAlive
  EXPECT_TRUE(profile_manager->HasKeepAliveForTesting(
      main_profile, ProfileKeepAliveOrigin::kLacrosMainProfile));
  // So the profile is not destroyed on browser close.
  EXPECT_FALSE(main_waiter.destroyed());

  // The secondary profile is destroyed on browser close.
  EXPECT_TRUE(secondary_waiter.destroyed());
}
#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)

#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)

// Tests Profile::GetAllOffTheRecordProfiles
IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, TestGetAllOffTheRecordProfiles) {}

// Tests Profile::IsSameOrParent
IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, TestIsSameOrParent) {}

// Tests if browser creation using non primary OTRs is blocked.
IN_PROC_BROWSER_TEST_F(ProfileBrowserTest,
                       TestCreatingBrowserUsingNonPrimaryOffTheRecordProfile) {}

// Tests if profile type returned by |profile_metrics::GetBrowserProfileType| is
// correct.
IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, TestProfileTypes) {}

#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)

IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, UnderOneMinute) {}

IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, OneHour) {}

#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)

#if BUILDFLAG(IS_CHROMEOS_LACROS)
IN_PROC_BROWSER_TEST_F(ProfileBrowserTest,
                       IsMainProfileReturnsFalseForNonDefaultPaths) {
  base::ScopedAllowBlockingForTesting allow_blocking;
  base::ScopedTempDir temp_dir;
  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());

  {
    base::FilePath profile_path = temp_dir.GetPath().Append("1Default");
    std::unique_ptr<Profile> profile(
        CreateProfile(profile_path, /* delegate= */ nullptr,
                      Profile::CreateMode::kSynchronous));

    EXPECT_FALSE(profile->IsMainProfile());

    // Creating a profile causes an implicit connection attempt to a Mojo
    // service, which occurs as part of a new task. Before deleting |profile|,
    // ensure this task runs to prevent a crash.
    FlushIoTaskRunnerAndSpinThreads();
  }
  FlushIoTaskRunnerAndSpinThreads();
}

IN_PROC_BROWSER_TEST_F(ProfileBrowserTest,
                       IsMainProfileReturnsTrueForPublicSessions) {
  base::ScopedAllowBlockingForTesting allow_blocking;
  base::ScopedTempDir temp_dir;
  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());

  {
    base::FilePath profile_path =
        temp_dir.GetPath().Append(chrome::kInitialProfile);
    std::unique_ptr<Profile> profile(
        CreateProfile(profile_path, /* delegate= */ nullptr,
                      Profile::CreateMode::kSynchronous));

    crosapi::mojom::BrowserInitParamsPtr init_params =
        crosapi::mojom::BrowserInitParams::New();
    init_params->session_type = crosapi::mojom::SessionType::kPublicSession;
    chromeos::BrowserInitParams::SetInitParamsForTests(std::move(init_params));

    EXPECT_EQ(chromeos::BrowserParamsProxy::Get()->SessionType(),
              crosapi::mojom::SessionType::kPublicSession);
    EXPECT_TRUE(profile->IsMainProfile());

    // Creating a profile causes an implicit connection attempt to a Mojo
    // service, which occurs as part of a new task. Before deleting |profile|,
    // ensure this task runs to prevent a crash.
    FlushIoTaskRunnerAndSpinThreads();
  }
  FlushIoTaskRunnerAndSpinThreads();
}

IN_PROC_BROWSER_TEST_F(ProfileBrowserTest,
                       IsMainProfileReturnsTrueForWebKioskSession) {
  base::ScopedAllowBlockingForTesting allow_blocking;
  base::ScopedTempDir temp_dir;
  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());

  {
    base::FilePath profile_path =
        temp_dir.GetPath().Append(chrome::kInitialProfile);
    std::unique_ptr<Profile> profile(
        CreateProfile(profile_path, /* delegate= */ nullptr,
                      Profile::CreateMode::kSynchronous));

    crosapi::mojom::BrowserInitParamsPtr init_params =
        crosapi::mojom::BrowserInitParams::New();
    init_params->session_type = crosapi::mojom::SessionType::kWebKioskSession;
    chromeos::BrowserInitParams::SetInitParamsForTests(std::move(init_params));

    EXPECT_EQ(chromeos::BrowserParamsProxy::Get()->SessionType(),
              crosapi::mojom::SessionType::kWebKioskSession);
    EXPECT_TRUE(profile->IsMainProfile());

    // Creating a profile causes an implicit connection attempt to a Mojo
    // service, which occurs as part of a new task. Before deleting |profile|,
    // ensure this task runs to prevent a crash.
    FlushIoTaskRunnerAndSpinThreads();
  }
  FlushIoTaskRunnerAndSpinThreads();
}

IN_PROC_BROWSER_TEST_F(
    ProfileBrowserTest,
    IsMainProfileReturnsTrueForMainProfileInRegularSessions) {
  const base::FilePath profile_path =
      browser()->profile()->GetPath().DirName().Append(chrome::kInitialProfile);
  Profile* profile =
      g_browser_process->profile_manager()->GetProfileByPath(profile_path);
  EXPECT_TRUE(profile->IsMainProfile());
}

IN_PROC_BROWSER_TEST_F(
    ProfileBrowserTest,
    IsMainProfileReturnsFalseForOTRProfileInRegularSessions) {
  const base::FilePath profile_path =
      browser()->profile()->GetPath().DirName().Append(chrome::kInitialProfile);
  Profile* profile =
      g_browser_process->profile_manager()->GetProfileByPath(profile_path);
  EXPECT_FALSE(profile->GetPrimaryOTRProfile(/*create_if_needed=*/true)
                   ->IsMainProfile());
}
#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)