// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <memory>
#include <string>
#include <utility>

#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/scoped_observation.h"
#include "base/strings/string_split.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/bind_post_task.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/bind.h"
#include "base/test/test_timeouts.h"
#include "base/threading/hang_watcher.h"
#include "build/build_config.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/browser/renderer_host/render_frame_proxy_host.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/renderer_host/render_process_host_internal_observer.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/common/content_navigation_policy.h"
#include "content/public/browser/back_forward_cache.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/child_process_launcher_utils.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_process_host_creation_observer.h"
#include "content/public/browser/render_process_host_observer.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/pseudonymization_util.h"
#include "content/public/common/url_constants.h"
#include "content/public/test/back_forward_cache_util.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_content_browser_client.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/no_renderer_crashes_assertion.h"
#include "content/public/test/test_frame_navigation_observer.h"
#include "content/public/test/test_service.mojom.h"
#include "content/public/test/test_utils.h"
#include "content/shell/browser/shell.h"
#include "content/shell/browser/shell_browser_context.h"
#include "content/shell/browser/shell_browser_main_parts.h"
#include "content/test/content_browser_test_utils_internal.h"
#include "content/test/storage_partition_test_helpers.h"
#include "media/base/media_switches.h"
#include "media/base/test_data_util.h"
#include "media/mojo/buildflags.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "net/dns/mock_host_resolver.h"
#include "net/http/http_status_code.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/public/common/chrome_debug_urls.h"

#if BUILDFLAG(IS_WIN)
#include "base/features.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "mojo/public/cpp/platform/platform_handle_security_util_win.h"
#include "sandbox/policy/switches.h"
#endif

namespace content {

namespace {

// Similar to net::test_server::DelayedHttpResponse, but the delay is resolved
// through Resolver.
class DelayedHttpResponseWithResolver final
    : public net::test_server::BasicHttpResponse {
 public:
  class Resolver final : public base::RefCountedThreadSafe<Resolver> {
   public:
    void Resolve() {
      base::AutoLock auto_lock(lock_);
      DCHECK(!resolved_);
      resolved_ = true;

      if (!task_runner_) {
        return;
      }

      for (auto& response : response_closures_)
        task_runner_->PostTask(FROM_HERE, std::move(response));

      response_closures_.clear();
    }

    void Add(base::OnceClosure response) {
      base::AutoLock auto_lock(lock_);

      if (resolved_) {
        std::move(response).Run();
        return;
      }

      scoped_refptr<base::SingleThreadTaskRunner> task_runner =
          base::SingleThreadTaskRunner::GetCurrentDefault();
      if (task_runner_) {
        DCHECK_EQ(task_runner_, task_runner);
      } else {
        task_runner_ = std::move(task_runner);
      }

      response_closures_.push_back(std::move(response));
    }

   private:
    friend class base::RefCountedThreadSafe<Resolver>;
    ~Resolver() = default;

    base::Lock lock_;

    std::vector<base::OnceClosure> response_closures_;
    bool resolved_ GUARDED_BY(lock_) = false;
    scoped_refptr<base::SingleThreadTaskRunner> task_runner_ GUARDED_BY(lock_);
  };

  explicit DelayedHttpResponseWithResolver(scoped_refptr<Resolver> resolver)
      : resolver_(std::move(resolver)) {}

  DelayedHttpResponseWithResolver(const DelayedHttpResponseWithResolver&) =
      delete;
  DelayedHttpResponseWithResolver& operator=(
      const DelayedHttpResponseWithResolver&) = delete;

  void SendResponse(
      base::WeakPtr<net::test_server::HttpResponseDelegate> delegate) override {
    resolver_->Add(base::BindOnce(
        &net::test_server::HttpResponseDelegate::SendHeadersContentAndFinish,
        delegate, code(), GetHttpReasonPhrase(code()), BuildHeaders(),
        content()));
  }

 private:
  const scoped_refptr<Resolver> resolver_;
};

std::unique_ptr<net::test_server::HttpResponse> HandleBeacon(
    const net::test_server::HttpRequest& request) {
  if (request.relative_url != "/beacon")
    return nullptr;
  return std::make_unique<net::test_server::BasicHttpResponse>();
}

std::unique_ptr<net::test_server::HttpResponse> HandleHungBeacon(
    const base::RepeatingClosure& on_called,
    const net::test_server::HttpRequest& request) {
  if (request.relative_url != "/beacon")
    return nullptr;
  if (on_called) {
    on_called.Run();
  }
  return std::make_unique<net::test_server::HungResponse>();
}

std::unique_ptr<net::test_server::HttpResponse> HandleHungBeaconWithResolver(
    scoped_refptr<DelayedHttpResponseWithResolver::Resolver> resolver,
    const net::test_server::HttpRequest& request) {
  if (request.relative_url != "/beacon")
    return nullptr;
  return std::make_unique<DelayedHttpResponseWithResolver>(std::move(resolver));
}

}  // namespace

class RenderProcessHostTestBase : public ContentBrowserTest,
                                  public RenderProcessHostObserver {
 public:
  RenderProcessHostTestBase() = default;

  void SetUpCommandLine(base::CommandLine* command_line) override {
    command_line->AppendSwitchASCII(
        switches::kAutoplayPolicy,
        switches::autoplay::kNoUserGestureRequiredPolicy);
  }

  void SetUpOnMainThread() override {
    // Support multiple sites on the test server.
    host_resolver()->AddRule("*", "127.0.0.1");
  }

  void SetVisibleClients(RenderProcessHost* process, int32_t visible_clients) {
    RenderProcessHostImpl* impl = static_cast<RenderProcessHostImpl*>(process);
    impl->visible_clients_ = visible_clients;
  }

 protected:
  void SetProcessExitCallback(RenderProcessHost* rph,
                              base::OnceClosure callback) {
    Observe(rph);
    process_exit_callback_ = std::move(callback);
  }

  void Observe(RenderProcessHost* rph) {
    DCHECK(!observation_.IsObserving());
    observation_.Observe(rph);
  }

  // RenderProcessHostObserver:
  void RenderProcessExited(RenderProcessHost* host,
                           const ChildProcessTerminationInfo& info) override {
    ++process_exits_;
    if (process_exit_callback_)
      std::move(process_exit_callback_).Run();
  }
  void RenderProcessHostDestroyed(RenderProcessHost* host) override {
    ++host_destructions_;
    observation_.Reset();
  }
  void WaitUntilProcessExits(int target) {
    while (process_exits_ < target)
      base::RunLoop().RunUntilIdle();
  }

  base::ScopedObservation<RenderProcessHost, RenderProcessHostObserver>
      observation_{this};
  int process_exits_ = 0;
  int host_destructions_ = 0;
  base::OnceClosure process_exit_callback_;
};

// A mock ContentBrowserClient that only considers a spare renderer to be a
// suitable host.
class SpareRendererContentBrowserClient
    : public ContentBrowserTestContentBrowserClient {
 public:
  bool IsSuitableHost(RenderProcessHost* process_host,
                      const GURL& site_url) override {
    if (RenderProcessHostImpl::GetSpareRenderProcessHostForTesting()) {
      return process_host ==
             RenderProcessHostImpl::GetSpareRenderProcessHostForTesting();
    }
    return true;
  }
};

// A mock ContentBrowserClient that only considers a non-spare renderer to be a
// suitable host, but otherwise tries to reuse processes.
class NonSpareRendererContentBrowserClient
    : public ContentBrowserTestContentBrowserClient {
 public:
  NonSpareRendererContentBrowserClient() = default;

  NonSpareRendererContentBrowserClient(
      const NonSpareRendererContentBrowserClient&) = delete;
  NonSpareRendererContentBrowserClient& operator=(
      const NonSpareRendererContentBrowserClient&) = delete;

  bool IsSuitableHost(RenderProcessHost* process_host,
                      const GURL& site_url) override {
    return RenderProcessHostImpl::GetSpareRenderProcessHostForTesting() !=
           process_host;
  }

  bool ShouldTryToUseExistingProcessHost(BrowserContext* context,
                                         const GURL& url) override {
    return true;
  }

  bool ShouldUseSpareRenderProcessHost(BrowserContext* browser_context,
                                       const GURL& site_url) override {
    return false;
  }
};

// A ContentBrowserClient that can wait for calls to
// `blink::mojom::KeepAliveHandle`.
class KeepAliveHandleContentBrowserClient
    : public ContentBrowserTestContentBrowserClient {
 public:
  explicit KeepAliveHandleContentBrowserClient(base::OnceClosure callback) {
    started_callback_ = std::move(callback);
  }
  void OnKeepaliveRequestStarted(BrowserContext* browser_context) override {
    ContentBrowserTestContentBrowserClient::OnKeepaliveRequestStarted(
        browser_context);
    CHECK(started_callback_);
    GetUIThreadTaskRunner({})->PostTask(FROM_HERE,
                                        std::move(started_callback_));
  }

 private:
  base::OnceClosure started_callback_;
};

class RenderProcessHostTest : public RenderProcessHostTestBase,
                              public ::testing::WithParamInterface<bool> {
 public:
  RenderProcessHostTest() = default;

  void SetUp() override {
    if (IsKeepAliveInBrowserMigrationEnabled()) {
      feature_list_.InitAndEnableFeature(
          blink::features::kKeepAliveInBrowserMigration);
    } else {
      feature_list_.InitAndDisableFeature(
          blink::features::kKeepAliveInBrowserMigration);
    }
    RenderProcessHostTestBase::SetUp();
  }

 protected:
  bool IsKeepAliveInBrowserMigrationEnabled() { return GetParam(); }

  base::test::ScopedFeatureList feature_list_;
};

INSTANTIATE_TEST_SUITE_P(
    All,
    RenderProcessHostTest,
    testing::Values(false, true),
    [](const testing::TestParamInfo<RenderProcessHostTest::ParamType>& info) {
      return info.param ? "KeepAliveInBrowserMigration" : "Default";
    });

IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, GuestsAreNotSuitableHosts) {
  // Set max renderers to 1 to force running out of processes.
  RenderProcessHost::SetMaxRendererProcessCount(1);

  ASSERT_TRUE(embedded_test_server()->Start());

  GURL test_url = embedded_test_server()->GetURL("/simple_page.html");
  EXPECT_TRUE(NavigateToURL(shell(), test_url));
  RenderProcessHost* rph =
      shell()->web_contents()->GetPrimaryMainFrame()->GetProcess();
  // Make it believe it's a guest.
  static_cast<RenderProcessHostImpl*>(rph)->SetForGuestsOnlyForTesting();
  EXPECT_EQ(1, RenderProcessHost::GetCurrentRenderProcessCountForTesting());

  // Navigate to a different page.
  GURL::Replacements replace_host;
  replace_host.SetHostStr("localhost");
  GURL another_url = embedded_test_server()->GetURL("/simple_page.html");
  another_url = another_url.ReplaceComponents(replace_host);
  EXPECT_TRUE(NavigateToURL(CreateBrowser(), another_url));

  // Expect that we got another process (the guest renderer was not reused).
  EXPECT_EQ(2, RenderProcessHost::GetCurrentRenderProcessCountForTesting());
}

IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, SpareRenderProcessHostTaken) {
  ASSERT_TRUE(embedded_test_server()->Start());

  RenderProcessHost::WarmupSpareRenderProcessHost(
      ShellContentBrowserClient::Get()->browser_context());
  RenderProcessHost* spare_renderer =
      RenderProcessHostImpl::GetSpareRenderProcessHostForTesting();
  EXPECT_NE(nullptr, spare_renderer);

  GURL test_url = embedded_test_server()->GetURL("/simple_page.html");
  Shell* window = CreateBrowser();
  EXPECT_TRUE(NavigateToURL(window, test_url));

  EXPECT_EQ(spare_renderer,
            window->web_contents()->GetPrimaryMainFrame()->GetProcess());

  // The old spare render process host should no longer be available.
  EXPECT_NE(spare_renderer,
            RenderProcessHostImpl::GetSpareRenderProcessHostForTesting());

  // Check if a fresh spare is available (depending on the operating mode).
  if (RenderProcessHostImpl::IsSpareProcessKeptAtAllTimes()) {
    EXPECT_NE(nullptr,
              RenderProcessHostImpl::GetSpareRenderProcessHostForTesting());
  } else {
    EXPECT_EQ(nullptr,
              RenderProcessHostImpl::GetSpareRenderProcessHostForTesting());
  }
}

IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, SpareRenderProcessHostNotTaken) {
  ASSERT_TRUE(embedded_test_server()->Start());

  RenderProcessHost::WarmupSpareRenderProcessHost(
      ShellContentBrowserClient::Get()->off_the_record_browser_context());
  RenderProcessHost* spare_renderer =
      RenderProcessHostImpl::GetSpareRenderProcessHostForTesting();
  GURL test_url = embedded_test_server()->GetURL("/simple_page.html");
  Shell* window = CreateBrowser();
  EXPECT_TRUE(NavigateToURL(window, test_url));

  // There should have been another process created for the navigation.
  EXPECT_NE(spare_renderer,
            window->web_contents()->GetPrimaryMainFrame()->GetProcess());

  // Check if a fresh spare is available (depending on the operating mode).
  // Note this behavior is identical to what would have happened if the
  // RenderProcessHost were taken.
  if (RenderProcessHostImpl::IsSpareProcessKeptAtAllTimes()) {
    EXPECT_NE(nullptr,
              RenderProcessHostImpl::GetSpareRenderProcessHostForTesting());
  } else {
    EXPECT_EQ(nullptr,
              RenderProcessHostImpl::GetSpareRenderProcessHostForTesting());
  }
}

IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, SpareRenderProcessHostKilled) {
  RenderProcessHost::WarmupSpareRenderProcessHost(
      ShellContentBrowserClient::Get()->browser_context());

  RenderProcessHost* spare_renderer =
      RenderProcessHostImpl::GetSpareRenderProcessHostForTesting();
  mojo::Remote<mojom::TestService> service;
  ASSERT_NE(nullptr, spare_renderer);
  spare_renderer->BindReceiver(service.BindNewPipeAndPassReceiver());

  base::RunLoop run_loop;
  SetProcessExitCallback(spare_renderer, run_loop.QuitClosure());

  // Should reply with a bad message and cause process death.
  {
    ScopedAllowRendererCrashes scoped_allow_renderer_crashes(spare_renderer);
    service->DoSomething(base::DoNothing());
    run_loop.Run();
  }

  // The spare RenderProcessHost should disappear when its process dies.
  EXPECT_EQ(nullptr,
            RenderProcessHostImpl::GetSpareRenderProcessHostForTesting());
}

// Test that the spare renderer works correctly when the limit on the maximum
// number of processes is small.
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest,
                       SpareRendererSurpressedMaxProcesses) {
  ASSERT_TRUE(embedded_test_server()->Start());

  SpareRendererContentBrowserClient browser_client;

  RenderProcessHost::SetMaxRendererProcessCount(1);

  // A process is created with shell startup, so with a maximum of one renderer
  // process the spare RPH should not be created.
  RenderProcessHost::WarmupSpareRenderProcessHost(
      ShellContentBrowserClient::Get()->browser_context());
  EXPECT_EQ(nullptr,
            RenderProcessHostImpl::GetSpareRenderProcessHostForTesting());

  // A spare RPH should be created with a max of 2 renderer processes.
  RenderProcessHost::SetMaxRendererProcessCount(2);
  RenderProcessHost::WarmupSpareRenderProcessHost(
      ShellContentBrowserClient::Get()->browser_context());
  RenderProcessHost* spare_renderer =
      RenderProcessHostImpl::GetSpareRenderProcessHostForTesting();
  EXPECT_NE(nullptr, spare_renderer);

  // Thanks to the injected SpareRendererContentBrowserClient and the limit on
  // processes, the spare RPH will always be used via GetExistingProcessHost()
  // rather than picked up via MaybeTakeSpareRenderProcessHost().
  GURL test_url = embedded_test_server()->GetURL("/simple_page.html");
  Shell* new_window = CreateBrowser();
  EXPECT_TRUE(NavigateToURL(new_window, test_url));
  // Outside of RenderProcessHostImpl::IsSpareProcessKeptAtAllTimes mode, the
  // spare RPH should have been dropped during CreateBrowser() and given to the
  // new window.  OTOH, even in the IsSpareProcessKeptAtAllTimes mode, the spare
  // shouldn't be created because of the low process limit.
  EXPECT_EQ(nullptr,
            RenderProcessHostImpl::GetSpareRenderProcessHostForTesting());
  EXPECT_EQ(spare_renderer,
            new_window->web_contents()->GetPrimaryMainFrame()->GetProcess());

  // Revert to the default process limit and original ContentBrowserClient.
  RenderProcessHost::SetMaxRendererProcessCount(0);
}

// Check that the spare renderer is dropped if an existing process is reused.
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, SpareRendererOnProcessReuse) {
  ASSERT_TRUE(embedded_test_server()->Start());

  NonSpareRendererContentBrowserClient browser_client;

  RenderProcessHost::WarmupSpareRenderProcessHost(
      ShellContentBrowserClient::Get()->browser_context());
  RenderProcessHost* spare_renderer =
      RenderProcessHostImpl::GetSpareRenderProcessHostForTesting();
  EXPECT_NE(nullptr, spare_renderer);

  // This should reuse the existing process.
  Shell* new_browser = CreateBrowser();
  EXPECT_EQ(shell()->web_contents()->GetPrimaryMainFrame()->GetProcess(),
            new_browser->web_contents()->GetPrimaryMainFrame()->GetProcess());
  EXPECT_NE(spare_renderer,
            new_browser->web_contents()->GetPrimaryMainFrame()->GetProcess());
  if (RenderProcessHostImpl::IsSpareProcessKeptAtAllTimes()) {
    EXPECT_NE(nullptr,
              RenderProcessHostImpl::GetSpareRenderProcessHostForTesting());
  } else {
    EXPECT_EQ(nullptr,
              RenderProcessHostImpl::GetSpareRenderProcessHostForTesting());
  }

  // The launcher thread reads state from browser_client, need to wait for it to
  // be done before resetting the browser client. crbug.com/742533.
  base::WaitableEvent launcher_thread_done(
      base::WaitableEvent::ResetPolicy::MANUAL,
      base::WaitableEvent::InitialState::NOT_SIGNALED);
  GetProcessLauncherTaskRunner()->PostTask(
      FROM_HERE,
      base::BindOnce([](base::WaitableEvent* done) { done->Signal(); },
                     base::Unretained(&launcher_thread_done)));
  ASSERT_TRUE(launcher_thread_done.TimedWait(TestTimeouts::action_timeout()));
}

// Verifies that the spare renderer maintained by SpareRenderProcessHostManager
// is correctly destroyed during browser shutdown.  This test is an analogue
// to the //chrome-layer FastShutdown.SpareRenderProcessHost test.
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest,
                       SpareRenderProcessHostDuringShutdown) {
  content::RenderProcessHost::WarmupSpareRenderProcessHost(
      shell()->web_contents()->GetBrowserContext());

  // The verification is that there are no DCHECKs anywhere during test tear
  // down.
}

// Verifies that the spare renderer maintained by SpareRenderProcessHostManager
// is correctly destroyed when closing the last content shell.
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, SpareRendererDuringClosing) {
  content::RenderProcessHost::WarmupSpareRenderProcessHost(
      shell()->web_contents()->GetBrowserContext());
  shell()->web_contents()->Close();

  // The verification is that there are no DCHECKs or UaF anywhere during test
  // tear down.
}

// This test verifies that SpareRenderProcessHostManager correctly accounts
// for StoragePartition differences when handing out the spare process.
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest,
                       SpareProcessVsCustomStoragePartition) {
  ASSERT_TRUE(embedded_test_server()->Start());

  // Provide custom storage partition for test sites.
  GURL test_url = embedded_test_server()->GetURL("a.com", "/simple_page.html");
  CustomStoragePartitionBrowserClient modified_client(GURL("http://a.com/"));

  BrowserContext* browser_context =
      ShellContentBrowserClient::Get()->browser_context();
  scoped_refptr<SiteInstance> test_site_instance =
      SiteInstance::CreateForURL(browser_context, test_url);
  StoragePartition* default_storage =
      browser_context->GetDefaultStoragePartition();
  StoragePartition* custom_storage =
      browser_context->GetStoragePartition(test_site_instance.get());
  EXPECT_NE(default_storage, custom_storage);

  // Open a test window - it should be associated with the default storage
  // partition.
  Shell* window = CreateBrowser();
  RenderProcessHost* old_process =
      window->web_contents()->GetPrimaryMainFrame()->GetProcess();
  EXPECT_EQ(default_storage, old_process->GetStoragePartition());

  // Warm up the spare process - it should be associated with the default
  // storage partition.
  RenderProcessHost::WarmupSpareRenderProcessHost(browser_context);
  RenderProcessHost* spare_renderer =
      RenderProcessHostImpl::GetSpareRenderProcessHostForTesting();
  ASSERT_TRUE(spare_renderer);
  EXPECT_EQ(default_storage, spare_renderer->GetStoragePartition());

  // Navigate to a URL that requires a custom storage partition.
  EXPECT_TRUE(NavigateToURL(window, test_url));
  RenderProcessHost* new_process =
      window->web_contents()->GetPrimaryMainFrame()->GetProcess();
  // Requirement to use a custom storage partition should force a process swap.
  EXPECT_NE(new_process, old_process);
  // The new process should be associated with the custom storage partition.
  EXPECT_EQ(custom_storage, new_process->GetStoragePartition());
  // And consequently, the spare shouldn't have been used.
  EXPECT_NE(spare_renderer, new_process);
}

class RenderProcessHostObserverCounter : public RenderProcessHostObserver {
 public:
  explicit RenderProcessHostObserverCounter(RenderProcessHost* host) {
    host->AddObserver(this);
    observing_ = true;
    observed_host_ = host;
  }

  RenderProcessHostObserverCounter(const RenderProcessHostObserverCounter&) =
      delete;
  RenderProcessHostObserverCounter& operator=(
      const RenderProcessHostObserverCounter&) = delete;

  ~RenderProcessHostObserverCounter() override {
    if (observing_)
      observed_host_->RemoveObserver(this);
  }

  void RenderProcessExited(RenderProcessHost* host,
                           const ChildProcessTerminationInfo& info) override {
    DCHECK(observing_);
    DCHECK_EQ(host, observed_host_);
    exited_count_++;
  }

  void RenderProcessHostDestroyed(RenderProcessHost* host) override {
    DCHECK(observing_);
    DCHECK_EQ(host, observed_host_);
    destroyed_count_++;

    host->RemoveObserver(this);
    observing_ = false;
    observed_host_ = nullptr;
  }

  int exited_count() const { return exited_count_; }
  int destroyed_count() const { return destroyed_count_; }

 private:
  int exited_count_ = 0;
  int destroyed_count_ = 0;
  bool observing_ = false;
  raw_ptr<RenderProcessHost> observed_host_ = nullptr;
};

// Check that the spare renderer is properly destroyed via DisableRefCounts().
// Note: DisableRefCounts() used to be called DisableKeepAliveRefCount();
// the name if this test is left unchanged to avoid disrupt any tracking
// tools (e.g. flakiness) that might reference the old name.
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, SpareVsDisableKeepAliveRefCount) {
  RenderProcessHost::WarmupSpareRenderProcessHost(
      ShellContentBrowserClient::Get()->browser_context());
  base::RunLoop().RunUntilIdle();

  RenderProcessHost* spare_renderer =
      RenderProcessHostImpl::GetSpareRenderProcessHostForTesting();
  RenderProcessHostObserverCounter counter(spare_renderer);

  RenderProcessHostWatcher process_watcher(
      spare_renderer, RenderProcessHostWatcher::WATCH_FOR_HOST_DESTRUCTION);

  spare_renderer->DisableRefCounts();

  process_watcher.Wait();
  EXPECT_TRUE(process_watcher.did_exit_normally());

  // An important part of test verification is that UaF doesn't happen in the
  // next revolution of the message pump - without extra care in the
  // SpareRenderProcessHostManager RenderProcessHost::Cleanup could be called
  // twice leading to a crash caused by double-free flavour of UaF in
  // base::DeleteHelper<...>::DoDelete.
  base::RunLoop().RunUntilIdle();

  DCHECK_EQ(1, counter.exited_count());
  DCHECK_EQ(1, counter.destroyed_count());
}

// Check that the spare renderer is properly destroyed via DisableRefCounts().
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, SpareVsFastShutdown) {
  RenderProcessHost::WarmupSpareRenderProcessHost(
      ShellContentBrowserClient::Get()->browser_context());
  base::RunLoop().RunUntilIdle();

  RenderProcessHost* spare_renderer =
      RenderProcessHostImpl::GetSpareRenderProcessHostForTesting();
  RenderProcessHostObserverCounter counter(spare_renderer);

  RenderProcessHostWatcher process_watcher(
      spare_renderer, RenderProcessHostWatcher::WATCH_FOR_HOST_DESTRUCTION);

  spare_renderer->FastShutdownIfPossible();

  process_watcher.Wait();
  EXPECT_FALSE(process_watcher.did_exit_normally());

  // An important part of test verification is that UaF doesn't happen in the
  // next revolution of the message pump - without extra care in the
  // SpareRenderProcessHostManager RenderProcessHost::Cleanup could be called
  // twice leading to a crash caused by double-free flavour of UaF in
  // base::DeleteHelper<...>::DoDelete.
  base::RunLoop().RunUntilIdle();

  DCHECK_EQ(1, counter.exited_count());
  DCHECK_EQ(1, counter.destroyed_count());
}

class ShellCloser : public RenderProcessHostObserver {
 public:
  ShellCloser(Shell* shell, std::string* logging_string)
      : shell_(shell), logging_string_(logging_string) {}

 protected:
  // RenderProcessHostObserver:
  void RenderProcessExited(RenderProcessHost* host,
                           const ChildProcessTerminationInfo& info) override {
    logging_string_->append("ShellCloser::RenderProcessExited ");
    shell_->Close();
  }

  void RenderProcessHostDestroyed(RenderProcessHost* host) override {
    logging_string_->append("ShellCloser::RenderProcessHostDestroyed ");
  }

  raw_ptr<Shell, DanglingUntriaged> shell_;
  raw_ptr<std::string> logging_string_;
};

class ObserverLogger : public RenderProcessHostObserver {
 public:
  explicit ObserverLogger(std::string* logging_string)
      : logging_string_(logging_string), host_destroyed_(false) {}

  bool host_destroyed() { return host_destroyed_; }

 protected:
  // RenderProcessHostObserver:
  void RenderProcessExited(RenderProcessHost* host,
                           const ChildProcessTerminationInfo& info) override {
    logging_string_->append("ObserverLogger::RenderProcessExited ");
  }

  void RenderProcessHostDestroyed(RenderProcessHost* host) override {
    logging_string_->append("ObserverLogger::RenderProcessHostDestroyed ");
    host_destroyed_ = true;
  }

  raw_ptr<std::string> logging_string_;
  bool host_destroyed_;
};

// Flaky on Android. http://crbug.com/759514.
#if BUILDFLAG(IS_ANDROID)
#define MAYBE_AllProcessExitedCallsBeforeAnyHostDestroyedCalls \
  DISABLED_AllProcessExitedCallsBeforeAnyHostDestroyedCalls
#else
#define MAYBE_AllProcessExitedCallsBeforeAnyHostDestroyedCalls \
  AllProcessExitedCallsBeforeAnyHostDestroyedCalls
#endif
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest,
                       MAYBE_AllProcessExitedCallsBeforeAnyHostDestroyedCalls) {
  ASSERT_TRUE(embedded_test_server()->Start());

  GURL test_url = embedded_test_server()->GetURL("/simple_page.html");
  EXPECT_TRUE(NavigateToURL(shell(), test_url));

  std::string logging_string;
  ShellCloser shell_closer(shell(), &logging_string);
  ObserverLogger observer_logger(&logging_string);
  RenderProcessHost* rph =
      shell()->web_contents()->GetPrimaryMainFrame()->GetProcess();

  // Ensure that the ShellCloser observer is first, so that it will have first
  // dibs on the ProcessExited callback.
  base::ScopedObservation<RenderProcessHost, RenderProcessHostObserver>
      observation_1(&shell_closer);
  base::ScopedObservation<RenderProcessHost, RenderProcessHostObserver>
      observation_2(&observer_logger);
  observation_1.Observe(rph);
  observation_2.Observe(rph);

  // This will crash the render process, and start all the callbacks.
  // We can't use NavigateToURL here since it accesses the shell() after
  // navigating, which the shell_closer deletes.
  ScopedAllowRendererCrashes scoped_allow_renderer_crashes(shell());
  NavigateToURLBlockUntilNavigationsComplete(shell(),
                                             GURL(blink::kChromeUICrashURL), 1);

  // The key here is that all the RenderProcessExited callbacks precede all the
  // RenderProcessHostDestroyed callbacks.
  EXPECT_EQ(
      "ShellCloser::RenderProcessExited "
      "ObserverLogger::RenderProcessExited "
      "ShellCloser::RenderProcessHostDestroyed "
      "ObserverLogger::RenderProcessHostDestroyed ",
      logging_string);
}

IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, KillProcessOnBadMojoMessage) {
  ASSERT_TRUE(embedded_test_server()->Start());

  GURL test_url = embedded_test_server()->GetURL("/simple_page.html");
  EXPECT_TRUE(NavigateToURL(shell(), test_url));
  RenderProcessHost* rph =
      shell()->web_contents()->GetPrimaryMainFrame()->GetProcess();

  host_destructions_ = 0;
  process_exits_ = 0;

  mojo::Remote<mojom::TestService> service;
  rph->BindReceiver(service.BindNewPipeAndPassReceiver());

  base::RunLoop run_loop;
  SetProcessExitCallback(rph, run_loop.QuitClosure());

  // Should reply with a bad message and cause process death.
  {
    ScopedAllowRendererCrashes scoped_allow_renderer_crashes(rph);
    service->DoSomething(base::DoNothing());
    run_loop.Run();
  }

  EXPECT_EQ(1, process_exits_);
  EXPECT_EQ(0, host_destructions_);
}

// Observes a WebContents and a specific frame within it, and waits until they
// both indicate that they are audible.
class AudioStartObserver : public WebContentsObserver {
 public:
  AudioStartObserver(WebContents* web_contents,
                     RenderFrameHost* render_frame_host,
                     base::OnceClosure audible_closure)
      : WebContentsObserver(web_contents),
        render_frame_host_(
            static_cast<RenderFrameHostImpl*>(render_frame_host)),
        contents_audible_(web_contents->IsCurrentlyAudible()),
        frame_audible_(render_frame_host_->is_audible()),
        audible_closure_(std::move(audible_closure)) {
    MaybeFireClosure();
  }
  ~AudioStartObserver() override = default;

  // WebContentsObserver:
  void OnAudioStateChanged(bool audible) override {
    DCHECK_NE(audible, contents_audible_);
    contents_audible_ = audible;
    MaybeFireClosure();
  }
  void OnFrameAudioStateChanged(RenderFrameHost* render_frame_host,
                                bool audible) override {
    if (render_frame_host_ != render_frame_host)
      return;
    DCHECK_NE(frame_audible_, audible);
    frame_audible_ = audible;
    MaybeFireClosure();
  }

 private:
  void MaybeFireClosure() {
    if (contents_audible_ && frame_audible_)
      std::move(audible_closure_).Run();
  }

  raw_ptr<RenderFrameHostImpl> render_frame_host_ = nullptr;
  bool contents_audible_ = false;
  bool frame_audible_ = false;
  base::OnceClosure audible_closure_;
};

// Tests that audio stream counts (used for process priority calculations) are
// properly set and cleared during media playback and renderer terminations.
//
// Note: This test can't run when the Mojo Renderer is used since it does not
// create audio streams through the normal audio pathways; at present this is
// only used by Chromecast.
//
// crbug.com/864476: flaky on Android for unclear reasons.
#if BUILDFLAG(ENABLE_MOJO_RENDERER) || BUILDFLAG(IS_ANDROID)
#define KillProcessZerosAudioStreams DISABLED_KillProcessZerosAudioStreams
#endif
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, KillProcessZerosAudioStreams) {
  // TODO(maxmorin): This test only uses an output stream. There should be a
  // similar test for input streams.
  embedded_test_server()->ServeFilesFromSourceDirectory(
      media::GetTestDataPath());
  ASSERT_TRUE(embedded_test_server()->Start());
  ASSERT_TRUE(NavigateToURL(
      shell(), embedded_test_server()->GetURL("/webaudio_oscillator.html")));
  RenderProcessHostImpl* rph = static_cast<RenderProcessHostImpl*>(
      shell()->web_contents()->GetPrimaryMainFrame()->GetProcess());

  {
    // Start audio and wait for it to become audible, in both the frame *and*
    // the page.
    base::RunLoop run_loop;
    AudioStartObserver observer(shell()->web_contents(),
                                shell()->web_contents()->GetPrimaryMainFrame(),
                                run_loop.QuitClosure());

    std::string result;
    EXPECT_EQ("OK", EvalJs(shell(), "StartOscillator();"))
        << "Failed to execute javascript.";
    run_loop.Run();

    // No point in running the rest of the test if this is wrong.
    ASSERT_EQ(1, rph->get_media_stream_count_for_testing());
  }

  host_destructions_ = 0;
  process_exits_ = 0;

  mojo::Remote<mojom::TestService> service;
  rph->BindReceiver(service.BindNewPipeAndPassReceiver());

  {
    // Force a bad message event to occur which will terminate the renderer.
    // Note: We post task the QuitClosure since RenderProcessExited() is called
    // before destroying BrowserMessageFilters; and the next portion of the test
    // must run after these notifications have been delivered.
    ScopedAllowRendererCrashes scoped_allow_renderer_crashes(rph);
    base::RunLoop run_loop;
    SetProcessExitCallback(rph, run_loop.QuitClosure());
    service->DoSomething(base::DoNothing());
    run_loop.Run();
  }

  {
    // Cycle UI and IO loop once to ensure OnChannelClosing() has been delivered
    // to audio stream owners and they get a chance to notify of stream closure.
    base::RunLoop run_loop;
    GetIOThreadTaskRunner({})->PostTask(
        FROM_HERE, base::BindPostTaskToCurrentDefault(run_loop.QuitClosure()));
    run_loop.Run();
  }

  // Verify shutdown went as expected.
  EXPECT_EQ(0, rph->get_media_stream_count_for_testing());
  EXPECT_EQ(1, process_exits_);
  EXPECT_EQ(0, host_destructions_);
}

// Test class instance to run specific setup steps for capture streams.
class CaptureStreamRenderProcessHostTest : public RenderProcessHostTestBase {
 public:
  void SetUp() override {
    // Pixel output is needed when digging pixel values out of video tags for
    // verification in VideoCaptureStream tests.
    EnablePixelOutput();
    RenderProcessHostTestBase::SetUp();
  }

  void SetUpCommandLine(base::CommandLine* command_line) override {
    // These flags are necessary to emulate camera input for getUserMedia()
    // tests.
    command_line->AppendSwitch(switches::kUseFakeUIForMediaStream);
    RenderProcessHostTestBase::SetUpCommandLine(command_line);
  }
};

// Tests that video capture stream count increments when getUserMedia() is
// called.
IN_PROC_BROWSER_TEST_F(CaptureStreamRenderProcessHostTest,
                       GetUserMediaIncrementsVideoCaptureStreams) {
  ASSERT_TRUE(embedded_test_server()->Start());
  EXPECT_TRUE(NavigateToURL(
      shell(), embedded_test_server()->GetURL("/media/getusermedia.html")));
  RenderProcessHostImpl* rph = static_cast<RenderProcessHostImpl*>(
      shell()->web_contents()->GetPrimaryMainFrame()->GetProcess());
  std::string result;
  EXPECT_TRUE(ExecJs(shell(), "getUserMediaAndExpectSuccess({video: true});"))
      << "Failed to execute javascript.";
  EXPECT_EQ(1, rph->get_media_stream_count_for_testing());
}

// Tests that video capture stream count resets when getUserMedia() is called
// and stopped.
IN_PROC_BROWSER_TEST_F(CaptureStreamRenderProcessHostTest,
                       StopResetsVideoCaptureStreams) {
  ASSERT_TRUE(embedded_test_server()->Start());
  EXPECT_TRUE(NavigateToURL(
      shell(), embedded_test_server()->GetURL("/media/getusermedia.html")));
  RenderProcessHostImpl* rph = static_cast<RenderProcessHostImpl*>(
      shell()->web_contents()->GetPrimaryMainFrame()->GetProcess());
  std::string result;
  EXPECT_TRUE(ExecJs(shell(), "getUserMediaAndStop({video: true});"))
      << "Failed to execute javascript.";
  EXPECT_EQ(0, rph->get_media_stream_count_for_testing());
}

// Tests that video capture stream counts (used for process priority
// calculations) are properly set and cleared during media playback and renderer
// terminations.
IN_PROC_BROWSER_TEST_F(CaptureStreamRenderProcessHostTest,
                       KillProcessZerosVideoCaptureStreams) {
  ASSERT_TRUE(embedded_test_server()->Start());
  EXPECT_TRUE(NavigateToURL(
      shell(), embedded_test_server()->GetURL("/media/getusermedia.html")));
  RenderProcessHostImpl* rph = static_cast<RenderProcessHostImpl*>(
      shell()->web_contents()->GetPrimaryMainFrame()->GetProcess());
  std::string result;
  EXPECT_TRUE(ExecJs(shell(), "getUserMediaAndExpectSuccess({video: true});"))
      << "Failed to execute javascript.";
  EXPECT_EQ(1, rph->get_media_stream_count_for_testing());

  host_destructions_ = 0;
  process_exits_ = 0;

  mojo::Remote<mojom::TestService> service;
  rph->BindReceiver(service.BindNewPipeAndPassReceiver());

  {
    // Force a bad message event to occur which will terminate the renderer.
    ScopedAllowRendererCrashes scoped_allow_renderer_crashes(rph);
    base::RunLoop run_loop;
    SetProcessExitCallback(rph, run_loop.QuitClosure());
    service->DoSomething(base::DoNothing());
    run_loop.Run();
  }

  {
    // Cycle UI and IO loop once to ensure OnChannelClosing() has been delivered
    // to audio stream owners and they get a chance to notify of stream closure.
    base::RunLoop run_loop;
    GetIOThreadTaskRunner({})->PostTask(
        FROM_HERE, base::BindPostTaskToCurrentDefault(run_loop.QuitClosure()));
    run_loop.Run();
  }

  EXPECT_EQ(0, rph->get_media_stream_count_for_testing());
  EXPECT_EQ(1, process_exits_);
  EXPECT_EQ(0, host_destructions_);
}

// Tests that media stream count increments when getUserMedia() is
// called with audio only.
IN_PROC_BROWSER_TEST_F(CaptureStreamRenderProcessHostTest,
                       GetUserMediaAudioOnlyIncrementsMediaStreams) {
  ASSERT_TRUE(embedded_test_server()->Start());
  EXPECT_TRUE(NavigateToURL(
      shell(), embedded_test_server()->GetURL("/media/getusermedia.html")));
  RenderProcessHostImpl* rph = static_cast<RenderProcessHostImpl*>(
      shell()->web_contents()->GetPrimaryMainFrame()->GetProcess());
  std::string result;
  EXPECT_TRUE(ExecJs(
      shell(), "getUserMediaAndExpectSuccess({video: false, audio: true});"))
      << "Failed to execute javascript.";
  EXPECT_EQ(1, rph->get_media_stream_count_for_testing());
}

// Tests that media stream counts (used for process priority
// calculations) are properly set and cleared during media playback and renderer
// terminations for audio only streams.
IN_PROC_BROWSER_TEST_F(CaptureStreamRenderProcessHostTest,
                       KillProcessZerosAudioCaptureStreams) {
  ASSERT_TRUE(embedded_test_server()->Start());
  EXPECT_TRUE(NavigateToURL(
      shell(), embedded_test_server()->GetURL("/media/getusermedia.html")));
  RenderProcessHostImpl* rph = static_cast<RenderProcessHostImpl*>(
      shell()->web_contents()->GetPrimaryMainFrame()->GetProcess());
  std::string result;
  EXPECT_TRUE(ExecJs(
      shell(), "getUserMediaAndExpectSuccess({video: false, audio: true});"))
      << "Failed to execute javascript.";
  EXPECT_EQ(1, rph->get_media_stream_count_for_testing());

  host_destructions_ = 0;
  process_exits_ = 0;

  mojo::Remote<mojom::TestService> service;
  rph->BindReceiver(service.BindNewPipeAndPassReceiver());

  {
    // Force a bad message event to occur which will terminate the renderer.
    ScopedAllowRendererCrashes scoped_allow_renderer_crashes(rph);
    base::RunLoop run_loop;
    SetProcessExitCallback(rph, run_loop.QuitClosure());
    service->DoSomething(base::DoNothing());
    run_loop.Run();
  }

  {
    // Cycle UI and IO loop once to ensure OnChannelClosing() has been delivered
    // to audio stream owners and they get a chance to notify of stream closure.
    base::RunLoop run_loop;
    GetIOThreadTaskRunner({})->PostTask(
        FROM_HERE, base::BindPostTaskToCurrentDefault(run_loop.QuitClosure()));
    run_loop.Run();
  }

  EXPECT_EQ(0, rph->get_media_stream_count_for_testing());
  EXPECT_EQ(1, process_exits_);
  EXPECT_EQ(0, host_destructions_);
}

IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, KeepAliveRendererProcess) {
  embedded_test_server()->RegisterRequestHandler(
      base::BindRepeating(HandleBeacon));
  ASSERT_TRUE(embedded_test_server()->Start());

  if (AreDefaultSiteInstancesEnabled()) {
    // Isolate "foo.com" so we are guaranteed that navigations to this site
    // will be in a different process.
    IsolateOriginsForTesting(embedded_test_server(), shell()->web_contents(),
                             {"foo.com"});
  }

  EXPECT_TRUE(NavigateToURL(
      shell(), embedded_test_server()->GetURL("/send-beacon.html")));

  RenderFrameHostImpl* rfh = static_cast<RenderFrameHostImpl*>(
      shell()->web_contents()->GetPrimaryMainFrame());
  RenderProcessHostImpl* rph =
      static_cast<RenderProcessHostImpl*>(rfh->GetProcess());

  // Disable the BackForwardCache to ensure the old process is going to be
  // released.
  DisableBackForwardCacheForTesting(shell()->web_contents(),
                                    BackForwardCache::TEST_REQUIRES_NO_CACHING);

  host_destructions_ = 0;
  process_exits_ = 0;
  Observe(rph);
  rfh->SetKeepAliveTimeoutForTesting(base::Seconds(30));

  if (IsKeepAliveInBrowserMigrationEnabled()) {
    // When fetch keepalive in browser migration is enabled, the process will be
    // able to exit immediately. Instead of verifying the time it takes until it
    // exits, verify that the `keep_alive_ref_count()` is as expected.
    EXPECT_EQ(rph->keep_alive_ref_count(), 0);
  }

  // Navigate to a site that will be in a different process.
  base::TimeTicks start = base::TimeTicks::Now();
  EXPECT_TRUE(NavigateToURL(
      shell(), embedded_test_server()->GetURL("foo.com", "/title1.html")));

  WaitUntilProcessExits(1);

  EXPECT_LT(base::TimeTicks::Now() - start, base::Seconds(30));
}

IN_PROC_BROWSER_TEST_P(RenderProcessHostTest,
                       KeepAliveRendererProcessWithServiceWorker) {
  if (IsKeepAliveInBrowserMigrationEnabled()) {
    // TODO(crbug.com/1356128): Add keepalive in-browser support for workers.
    return;
  }

  base::RunLoop run_loop;
  embedded_test_server()->RegisterRequestHandler(
      base::BindRepeating(HandleHungBeacon, run_loop.QuitClosure()));
  ASSERT_TRUE(embedded_test_server()->Start());

  EXPECT_TRUE(NavigateToURL(
      shell(),
      embedded_test_server()->GetURL("/workers/service_worker_setup.html")));
  EXPECT_EQ("ok", EvalJs(shell(), "setup();"));

  RenderProcessHostImpl* rph = static_cast<RenderProcessHostImpl*>(
      shell()->web_contents()->GetPrimaryMainFrame()->GetProcess());
  // 1 for the service worker.
  EXPECT_EQ(rph->worker_ref_count(), 1);
  EXPECT_EQ(rph->keep_alive_ref_count(), 0);

  // We use /workers/send-beacon.html, not send-beacon.html, due to the
  // service worker scope rule.
  EXPECT_TRUE(NavigateToURL(
      shell(), embedded_test_server()->GetURL("/workers/send-beacon.html")));

  run_loop.Run();
  // We are still using the same process.
  ASSERT_EQ(shell()->web_contents()->GetPrimaryMainFrame()->GetProcess(), rph);
  // 1 for the service worker, 1 for the keepalive fetch.
  if (!IsKeepAliveInBrowserMigrationEnabled()) {
    EXPECT_EQ(rph->keep_alive_ref_count(), 1);
  } else {
    // When fetch keepalive in browser migration is enabled, the process will be
    // able to exit immediately.
    EXPECT_EQ(rph->keep_alive_ref_count(), 0);
  }
  EXPECT_EQ(rph->worker_ref_count(), 1);
}

// Test is flaky on Android builders: https://crbug.com/875179
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_WIN)
#define MAYBE_KeepAliveRendererProcess_Hung \
  DISABLED_KeepAliveRendererProcess_Hung
#else
#define MAYBE_KeepAliveRendererProcess_Hung KeepAliveRendererProcess_Hung
#endif
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest,
                       MAYBE_KeepAliveRendererProcess_Hung) {
  // Disable HangWatcher so it doesn't interfere with this test when hangs take
  // place.
  base::HangWatcher::StopMonitoringForTesting();

  // The test assumes that the render process exits after 1 second. But this
  // will be prevented if the process still hosts a bfcached page. So disable
  // BFCache for this test.
  content::DisableBackForwardCacheForTesting(
      shell()->web_contents(),
      content::BackForwardCache::TEST_REQUIRES_NO_CACHING);

  embedded_test_server()->RegisterRequestHandler(
      base::BindRepeating(HandleHungBeacon, base::RepeatingClosure()));
  ASSERT_TRUE(embedded_test_server()->Start());

  const auto kTestUrl = embedded_test_server()->GetURL("/send-beacon.html");
  if (IsIsolatedOriginRequiredToGuaranteeDedicatedProcess()) {
    // Isolate host so that the first and second navigation are guaranteed to
    // be in different processes.
    IsolateOriginsForTesting(embedded_test_server(), shell()->web_contents(),
                             {kTestUrl.host()});
  }
  EXPECT_TRUE(NavigateToURL(shell(), kTestUrl));

  RenderFrameHostImpl* rfh = static_cast<RenderFrameHostImpl*>(
      shell()->web_contents()->GetPrimaryMainFrame());
  RenderProcessHostImpl* rph =
      static_cast<RenderProcessHostImpl*>(rfh->GetProcess());

  // Disable the BackForwardCache to ensure the old process is going to be
  // released.
  DisableBackForwardCacheForTesting(shell()->web_contents(),
                                    BackForwardCache::TEST_REQUIRES_NO_CACHING);
  host_destructions_ = 0;
  process_exits_ = 0;
  Observe(rph);
  rfh->SetKeepAliveTimeoutForTesting(base::Seconds(1));

  if (IsKeepAliveInBrowserMigrationEnabled()) {
    // When fetch keepalive in browser migration is enabled, the process will be
    // able to exit immediately. Instead of verifying the time it takes until it
    // exits, verify that the `keep_alive_ref_count()` is as expected.
    EXPECT_EQ(rph->keep_alive_ref_count(), 0);
  }

  base::TimeTicks start = base::TimeTicks::Now();
  EXPECT_TRUE(NavigateToURL(shell(), GURL("data:text/html,<p>hello</p>")));

  WaitUntilProcessExits(1);

  if (!IsKeepAliveInBrowserMigrationEnabled()) {
    EXPECT_GE(base::TimeTicks::Now() - start, base::Seconds(1));
  }
}

// Test is flaky on Android builders: https://crbug.com/875179
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_WIN)
#define MAYBE_FetchKeepAliveRendererProcess_Hung \
  DISABLED_FetchKeepAliveRendererProcess_Hung
#else
#define MAYBE_FetchKeepAliveRendererProcess_Hung \
  FetchKeepAliveRendererProcess_Hung
#endif
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest,
                       MAYBE_FetchKeepAliveRendererProcess_Hung) {
  // Disable HangWatcher so it doesn't interfere with this test when hangs take
  // place.
  base::HangWatcher::StopMonitoringForTesting();

  // The test assumes that the render process exits after 1 second. But this
  // will be prevented if the process still hosts a bfcached page. So disable
  // BFCache for this test.
  content::DisableBackForwardCacheForTesting(
      shell()->web_contents(),
      content::BackForwardCache::TEST_REQUIRES_NO_CACHING);

  embedded_test_server()->RegisterRequestHandler(
      base::BindRepeating(HandleHungBeacon, base::RepeatingClosure()));
  ASSERT_TRUE(embedded_test_server()->Start());

  const auto kTestUrl = embedded_test_server()->GetURL("/fetch-keepalive.html");
  if (IsIsolatedOriginRequiredToGuaranteeDedicatedProcess()) {
    // Isolate host so that the first and second navigation are guaranteed to
    // be in different processes.
    IsolateOriginsForTesting(embedded_test_server(), shell()->web_contents(),
                             {kTestUrl.host()});
  }

  EXPECT_TRUE(NavigateToURL(shell(), kTestUrl));

  RenderFrameHostImpl* rfh = static_cast<RenderFrameHostImpl*>(
      shell()->web_contents()->GetPrimaryMainFrame());
  RenderProcessHostImpl* rph =
      static_cast<RenderProcessHostImpl*>(rfh->GetProcess());

  // Disable the BackForwardCache to ensure the old process is going to be
  // released.
  DisableBackForwardCacheForTesting(shell()->web_contents(),
                                    BackForwardCache::TEST_REQUIRES_NO_CACHING);

  host_destructions_ = 0;
  process_exits_ = 0;
  Observe(rph);
  rfh->SetKeepAliveTimeoutForTesting(base::Seconds(1));

  if (IsKeepAliveInBrowserMigrationEnabled()) {
    // Wait for the page to make the keepalive request.
    const std::u16string waiting = u"Waiting";
    TitleWatcher watcher(shell()->web_contents(), waiting);
    ASSERT_EQ(waiting, watcher.WaitAndGetTitle());
    // When fetch keepalive in browser migration is enabled, the process will be
    // able to exit immediately. Instead of verifying the time it takes until it
    // exits, verify that the `keep_alive_ref_count()` is as expected.
    EXPECT_EQ(rph->keep_alive_ref_count(), 0);
  }

  base::TimeTicks start = base::TimeTicks::Now();
  EXPECT_TRUE(NavigateToURL(shell(), GURL("data:text/html,<p>hello</p>")));

  WaitUntilProcessExits(1);

  if (!IsKeepAliveInBrowserMigrationEnabled()) {
    EXPECT_GE(base::TimeTicks::Now() - start, base::Seconds(1));
  }
}

IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, ManyKeepaliveRequests) {
  auto resolver =
      base::MakeRefCounted<DelayedHttpResponseWithResolver::Resolver>();
  embedded_test_server()->RegisterRequestHandler(
      base::BindRepeating(HandleHungBeaconWithResolver, resolver));
  const std::u16string title = u"Resolved";
  const std::u16string waiting = u"Waiting";

  ASSERT_TRUE(embedded_test_server()->Start());
  EXPECT_TRUE(NavigateToURL(
      shell(),
      embedded_test_server()->GetURL("/fetch-keepalive.html?requests=256")));

  {
    // Wait for the page to make all the keepalive requests.
    TitleWatcher watcher(shell()->web_contents(), waiting);
    EXPECT_EQ(waiting, watcher.WaitAndGetTitle());
  }

  resolver->Resolve();

  {
    TitleWatcher watcher(shell()->web_contents(), title);
    EXPECT_EQ(title, watcher.WaitAndGetTitle());
  }
}

IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, TooManyKeepaliveRequests) {
  embedded_test_server()->RegisterRequestHandler(
      base::BindRepeating(HandleHungBeacon, base::RepeatingClosure()));
  ASSERT_TRUE(embedded_test_server()->Start());
  const std::u16string title = u"Rejected";

  TitleWatcher watcher(shell()->web_contents(), title);

  EXPECT_TRUE(NavigateToURL(
      shell(),
      embedded_test_server()->GetURL("/fetch-keepalive.html?requests=257")));

  EXPECT_EQ(title, watcher.WaitAndGetTitle());
}

// Records the value of |host->IsProcessBackgrounded()| when it changes.
// |host| must remain a valid reference for the lifetime of this object.
class IsProcessBackgroundedObserver : public RenderProcessHostInternalObserver {
 public:
  explicit IsProcessBackgroundedObserver(RenderProcessHostImpl* host) {
    host_observation_.Observe(host);
  }

  void RenderProcessBackgroundedChanged(RenderProcessHostImpl* host) override {
    backgrounded_ = host->IsProcessBackgrounded();
  }

  // Returns the latest recorded value if there was one and resets the recorded
  // value to |nullopt|.
  absl::optional<bool> TakeValue() {
    auto value = backgrounded_;
    backgrounded_ = absl::nullopt;
    return value;
  }

 private:
  // Stores the last observed value of IsProcessBackgrounded for a host.
  absl::optional<bool> backgrounded_;
  base::ScopedObservation<RenderProcessHostImpl,
                          RenderProcessHostInternalObserver>
      host_observation_{this};
};

IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, PriorityOverride) {
  // Start up a real renderer process.
  ASSERT_TRUE(embedded_test_server()->Start());
  GURL test_url = embedded_test_server()->GetURL("/simple_page.html");
  EXPECT_TRUE(NavigateToURL(shell(), test_url));
  RenderProcessHost* rph =
      shell()->web_contents()->GetPrimaryMainFrame()->GetProcess();
  RenderProcessHostImpl* process = static_cast<RenderProcessHostImpl*>(rph);

  IsProcessBackgroundedObserver observer(process);

  // It starts off as normal priority with no override.
  EXPECT_FALSE(process->HasPriorityOverride());
  EXPECT_FALSE(process->IsProcessBackgrounded());
  EXPECT_FALSE(observer.TakeValue().has_value());

  process->SetPriorityOverride(false /* foreground */);
  EXPECT_TRUE(process->HasPriorityOverride());
  EXPECT_TRUE(process->IsProcessBackgrounded());
  EXPECT_EQ(observer.TakeValue().value(), process->IsProcessBackgrounded());

  process->SetPriorityOverride(true /* foreground */);
  EXPECT_TRUE(process->HasPriorityOverride());
  EXPECT_FALSE(process->IsProcessBackgrounded());
  EXPECT_EQ(observer.TakeValue().value(), process->IsProcessBackgrounded());

  process->SetPriorityOverride(false /* foreground */);
  EXPECT_TRUE(process->HasPriorityOverride());
  EXPECT_TRUE(process->IsProcessBackgrounded());
  EXPECT_EQ(observer.TakeValue().value(), process->IsProcessBackgrounded());

  // Add a pending view, and expect the process to *stay* backgrounded.
  process->AddPendingView();
  EXPECT_TRUE(process->HasPriorityOverride());
  EXPECT_TRUE(process->IsProcessBackgrounded());
  EXPECT_FALSE(observer.TakeValue().has_value());

  // Clear the override. The pending view should cause the process to go back to
  // being foregrounded.
  process->ClearPriorityOverride();
  EXPECT_FALSE(process->HasPriorityOverride());
  EXPECT_FALSE(process->IsProcessBackgrounded());
  EXPECT_EQ(observer.TakeValue().value(), process->IsProcessBackgrounded());

  // Clear the pending view so the test doesn't explode.
  process->RemovePendingView();
}

// This test verifies properties of RenderProcessHostImpl *before* Init method
// is called.
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, ConstructedButNotInitializedYet) {
  RenderProcessHost* process = RenderProcessHostImpl::CreateRenderProcessHost(
      ShellContentBrowserClient::Get()->browser_context(), nullptr);

  // Just verifying that the arguments of CreateRenderProcessHost got processed
  // correctly.
  EXPECT_EQ(ShellContentBrowserClient::Get()->browser_context(),
            process->GetBrowserContext());
  EXPECT_FALSE(process->IsForGuestsOnly());

  // There should be no OS process before Init() method is called.
  EXPECT_FALSE(process->IsInitializedAndNotDead());
  EXPECT_FALSE(process->IsReady());
  EXPECT_FALSE(process->GetProcess().IsValid());
  EXPECT_EQ(base::kNullProcessHandle, process->GetProcess().Handle());

  // TODO(lukasza): https://crbug.com/813045: RenderProcessHost shouldn't have
  // an associated IPC channel (and shouldn't accumulate IPC messages) unless
  // the Init() method was called and the RPH either has connection to an actual
  // OS process or is currently attempting to spawn the OS process.  After this
  // bug is fixed the 1st test assertion below should be reversed (unsure about
  // the 2nd one).
  EXPECT_TRUE(process->GetChannel());
  EXPECT_TRUE(process->GetRendererInterface());

  // Cleanup the resources acquired by the test.
  process->Cleanup();
}

// This test verifies that a fast shutdown is possible for a starting process.
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, FastShutdownForStartingProcess) {
  RenderProcessHost* process = RenderProcessHostImpl::CreateRenderProcessHost(
      ShellContentBrowserClient::Get()->browser_context(), nullptr);
  process->Init();
  EXPECT_TRUE(process->FastShutdownIfPossible());
  process->Cleanup();
}

// Verifies that a fast shutdown is possible with pending keepalive request.
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest,
                       FastShutdownWithKeepAliveRequest) {
  base::RunLoop request_sent_loop, request_handled_loop;
  KeepAliveHandleContentBrowserClient browser_client(
      request_handled_loop.QuitClosure());
  embedded_test_server()->RegisterRequestHandler(
      base::BindRepeating(HandleHungBeacon, request_sent_loop.QuitClosure()));
  ASSERT_TRUE(embedded_test_server()->Start());

  const auto kTestUrl = embedded_test_server()->GetURL("/send-beacon.html");
  ASSERT_TRUE(NavigateToURL(shell(), kTestUrl));
  RenderFrameHostImpl* rfh = static_cast<RenderFrameHostImpl*>(
      shell()->web_contents()->GetPrimaryMainFrame());
  RenderProcessHostImpl* rph =
      static_cast<RenderProcessHostImpl*>(rfh->GetProcess());
  // Ensure keepalive request is sent.
  request_sent_loop.Run();

  if (IsKeepAliveInBrowserMigrationEnabled()) {
    // When fetch keepalive in browser migration is enabled, the process will be
    // able to exit immediately. Verify that the `keep_alive_ref_count()` is as
    // expected.
    EXPECT_EQ(rph->keep_alive_ref_count(), 0);
    EXPECT_TRUE(rph->FastShutdownIfPossible());
  } else {
    request_handled_loop.Run();
    EXPECT_EQ(rph->keep_alive_ref_count(), 1);
    EXPECT_FALSE(rph->FastShutdownIfPossible());
  }
}

// Tests that all RenderFrameHosts that lives in the process are accessible via
// RenderProcessHost::ForEachRenderFrameHost(), except those RenderFrameHosts
// whose lifecycle states are kSpeculative.
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, ForEachRenderFrameHost) {
  ASSERT_TRUE(embedded_test_server()->Start());
  GURL url_a = embedded_test_server()->GetURL(
      "a.com", "/cross_site_iframe_factory.html?a(a(a,a))");
  GURL url_b = embedded_test_server()->GetURL("b.com", "/title1.html");
  GURL url_new_window = embedded_test_server()->GetURL(
      "c.com", "/cross_site_iframe_factory.html?c(a)");
  IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());

  // 1. Navigate to `url_a`; it creates 4 RenderFrameHosts that live in the
  // same process.
  ASSERT_TRUE(NavigateToURL(shell(), url_a));
  RenderFrameHost* rfh_a = shell()->web_contents()->GetPrimaryMainFrame();
  std::vector<RenderFrameHost*> process_a_frames =
      CollectAllRenderFrameHosts(rfh_a);

  // 2. Open a new window, and navigate to a page with an a.com subframe.
  // The new subframe should reuse the same a.com process.
  Shell* new_window = CreateBrowser();
  ASSERT_TRUE(NavigateToURL(new_window, url_new_window));
  auto* new_window_main_frame = static_cast<RenderFrameHostImpl*>(
      new_window->web_contents()->GetPrimaryMainFrame());
  ASSERT_EQ(new_window_main_frame->child_count(), 1u);
  RenderFrameHost* new_window_sub_frame =
      new_window_main_frame->child_at(0)->current_frame_host();
  ASSERT_EQ(rfh_a->GetProcess(), new_window_sub_frame->GetProcess());
  process_a_frames.push_back(new_window_sub_frame);

  // 3. Start to navigate to a cross-origin site, and hold the navigation
  // request. This behavior will create a speculative RenderFrameHost.
  WebContentsImpl* web_contents =
      static_cast<WebContentsImpl*>(shell()->web_contents());
  TestNavigationManager manager(web_contents, url_b);
  shell()->LoadURL(url_b);
  ASSERT_TRUE(manager.WaitForRequestStart());

  // 4. Get the speculative RenderFrameHost.
  FrameTreeNode* root = web_contents->GetPrimaryFrameTree().root();
  RenderFrameHostImpl* rfh_b = root->render_manager()->speculative_frame_host();
  ASSERT_TRUE(rfh_b);
  EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kSpeculative,
            rfh_b->lifecycle_state());

  auto non_speculative_rfh_collector =
      [](std::vector<RenderFrameHost*>& results, RenderFrameHost* rfh) {
        auto* rfhi = static_cast<RenderFrameHostImpl*>(rfh);
        EXPECT_NE(RenderFrameHostImpl::LifecycleStateImpl::kSpeculative,
                  rfhi->lifecycle_state());
        results.push_back(rfh);
      };

  // 5. Check all of the a.com RenderFrameHosts are tracked by `rph_a`.
  RenderProcessHostImpl* rph_a =
      static_cast<RenderProcessHostImpl*>(rfh_a->GetProcess());
  std::vector<RenderFrameHost*> same_process_rfhs;
  rph_a->ForEachRenderFrameHost(base::BindRepeating(
      non_speculative_rfh_collector, std::ref(same_process_rfhs)));
  EXPECT_EQ(5, rph_a->GetRenderFrameHostCount());
  EXPECT_EQ(5u, same_process_rfhs.size());
  EXPECT_THAT(same_process_rfhs,
              testing::UnorderedElementsAreArray(process_a_frames.data(),
                                                 process_a_frames.size()));

  // 6. Check the speculative RenderFrameHost is ignored.
  same_process_rfhs.clear();
  RenderProcessHostImpl* rph_b =
      static_cast<RenderProcessHostImpl*>(rfh_b->GetProcess());
  ASSERT_NE(rph_a, rph_b);
  rph_b->ForEachRenderFrameHost(base::BindRepeating(
      non_speculative_rfh_collector, std::ref(same_process_rfhs)));
  EXPECT_EQ(1, rph_b->GetRenderFrameHostCount());
  // The speculative RenderFrameHost should be filtered out.
  EXPECT_EQ(same_process_rfhs.size(), 0u);

  // 7. Resume the blocked navigation.
  ASSERT_TRUE(manager.WaitForNavigationFinished());

  // 8. Check that `RenderProcessHost::ForEachRenderFrameHost` does not filter
  // `rfh_b` out, because its lifecycle has changed to kActive.
  EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kActive,
            rfh_b->lifecycle_state());

  EXPECT_EQ(1, rph_b->GetRenderFrameHostCount());
  same_process_rfhs.clear();
  rph_b->ForEachRenderFrameHost(base::BindRepeating(
      non_speculative_rfh_collector, std::ref(same_process_rfhs)));
  EXPECT_EQ(1, rph_b->GetRenderFrameHostCount());
  EXPECT_EQ(1u, same_process_rfhs.size());
  EXPECT_THAT(same_process_rfhs, testing::ElementsAre(rfh_b));
}

namespace {

// Observer that listens for process leak cleanup events. Note that this only
// hears about cases where the affected RenderViewHost is for the primary main
// frame.
class LeakCleanupObserver : public WebContentsObserver {
 public:
  explicit LeakCleanupObserver(WebContents* web_contents)
      : WebContentsObserver(web_contents) {}
  ~LeakCleanupObserver() override = default;

  // WebContentsObserver:
  void PrimaryMainFrameRenderProcessGone(
      base::TerminationStatus status) override {
    termination_count_++;
    CHECK_EQ(status,
             base::TerminationStatus::TERMINATION_STATUS_NORMAL_TERMINATION);
  }

  int termination_count() { return termination_count_; }

  void reset_termination_count() { termination_count_ = 0; }

 private:
  int termination_count_ = 0;
};

// Observer that listens for process exits and counts when they are treated as
// fast shutdown cases.
class FastShutdownExitObserver : public RenderProcessHostObserver {
 public:
  explicit FastShutdownExitObserver(RenderProcessHost* process) {
    process->AddObserver(this);
  }
  ~FastShutdownExitObserver() override = default;

  // RenderProcessHostObserver:
  void RenderProcessExited(RenderProcessHost* host,
                           const ChildProcessTerminationInfo& info) override {
    if (host->FastShutdownStarted())
      fast_shutdown_exit_count_++;
  }

  void RenderProcessHostDestroyed(RenderProcessHost* host) override {
    host->RemoveObserver(this);
  }

  int fast_shutdown_exit_count() { return fast_shutdown_exit_count_; }

  void reset_exit_count() { fast_shutdown_exit_count_ = 0; }

 private:
  int fast_shutdown_exit_count_ = 0;
};

}  // namespace

// Ensure that we don't leak a renderer process if there are only non-live
// RenderFrameHosts assigned to its RenderProcessHost (e.g., when the last live
// frame goes away). See https://crbug.com/1226834.
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, AllowUnusedProcessToExit) {
  ASSERT_TRUE(embedded_test_server()->Start());

  // Ensure all sites get dedicated processes during the test.
  IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());

  // Set max renderers to 1 to force reusing a renderer process between two
  // unrelated tabs.
  RenderProcessHost::SetMaxRendererProcessCount(1);

  // The test assumes that the render process exits after navigation, but this
  // will be prevented if the process still hosts a bfcached page. Disable
  // BFCache for this test.
  content::DisableBackForwardCacheForTesting(
      shell()->web_contents(),
      content::BackForwardCache::TEST_REQUIRES_NO_CACHING);

  // Ensure the initial tab has not loaded yet.
  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
                            ->GetPrimaryFrameTree()
                            .root();
  RenderFrameHostImpl* original_rfh = root->current_frame_host();
  RenderProcessHost* original_process = original_rfh->GetProcess();
  int original_process_id = original_process->GetID();
  EXPECT_FALSE(original_process->IsInitializedAndNotDead());
  EXPECT_FALSE(original_rfh->IsRenderFrameLive());

  // Reset the process exit related counts and listen to process exit events.
  process_exits_ = 0;
  host_destructions_ = 0;
  Observe(original_process);
  FastShutdownExitObserver fast_shutdown_observer(original_process);

  // Create a second tab to a real URL. This will share the first
  // RenderProcessHost due to the low limit, and because it was not used or
  // locked.
  GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
  Shell* shell2 =
      Shell::CreateNewWindow(shell()->web_contents()->GetBrowserContext(),
                             url_a, nullptr, gfx::Size());
  FrameTreeNode* root2 = static_cast<WebContentsImpl*>(shell2->web_contents())
                             ->GetPrimaryFrameTree()
                             .root();
  RenderFrameHostImpl* rfh2 = root2->current_frame_host();
  EXPECT_EQ(original_process, rfh2->GetProcess());
  EXPECT_TRUE(original_process->IsInitializedAndNotDead());
  EXPECT_TRUE(rfh2->IsRenderFrameLive());

  // The original RFH is still in an unloaded state.
  EXPECT_FALSE(original_rfh->IsRenderFrameLive());

  // Close shell2. This used to leave the process running, since original_rfh
  // was still counted as an active frame in the RenderProcessHost even though
  // it wasn't live.
  LeakCleanupObserver leak_cleanup_observer(shell()->web_contents());
  RenderProcessHostWatcher exit_observer(
      original_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
  RenderFrameDeletedObserver rfh2_deleted_observer(rfh2);
  shell2->Close();
  rfh2_deleted_observer.WaitUntilDeleted();
  exit_observer.Wait();
  EXPECT_TRUE(exit_observer.did_exit_normally());
  EXPECT_EQ(1, process_exits_);
  EXPECT_EQ(0, host_destructions_);
  EXPECT_EQ(1, leak_cleanup_observer.termination_count());

  // This cleanup should be considered similar to fast shutdown, for observers
  // that treat that case as expected behavior.
  EXPECT_EQ(1, fast_shutdown_observer.fast_shutdown_exit_count());

  EXPECT_EQ(original_rfh, root->current_frame_host());
  EXPECT_EQ(original_process, original_rfh->GetProcess());
  EXPECT_FALSE(original_process->IsInitializedAndNotDead());
  EXPECT_FALSE(original_rfh->IsRenderFrameLive());

  // There shouldn't be live RenderViewHosts or proxies either.
  EXPECT_FALSE(original_rfh->render_view_host()->IsRenderViewLive());
  EXPECT_FALSE(
      root->current_frame_host()
          ->browsing_context_state()
          ->GetRenderFrameProxyHost(original_rfh->GetSiteInstance()->group()));

  // Reset the process exit related counts.
  process_exits_ = 0;
  host_destructions_ = 0;
  leak_cleanup_observer.reset_termination_count();
  fast_shutdown_observer.reset_exit_count();

  // After the leak cleanup, navigate the original frame to the same site to
  // make sure it still works in the original RenderProcessHost, even though it
  // swaps to a new RenderFrameHost.
  EXPECT_TRUE(NavigateToURL(shell(), url_a));
  RenderFrameHostImpl* replaced_rfh = root->current_frame_host();
  ASSERT_EQ(original_process, replaced_rfh->GetProcess());
  EXPECT_EQ(original_process_id, replaced_rfh->GetProcess()->GetID());
  EXPECT_TRUE(replaced_rfh->GetProcess()->IsInitializedAndNotDead());
  EXPECT_TRUE(replaced_rfh->IsRenderFrameLive());
  EXPECT_FALSE(original_process->FastShutdownStarted());
  EXPECT_EQ(0, process_exits_);
  EXPECT_EQ(0, host_destructions_);
  EXPECT_EQ(0, fast_shutdown_observer.fast_shutdown_exit_count());
  EXPECT_EQ(0, leak_cleanup_observer.termination_count());

  // Ensure that the leak cleanup does not occur after a normal cross-process
  // navigation, since normal process cleanup generates different events,
  // including the full destruction of the RenderProcessHost.
  GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
  RenderProcessHostWatcher cleanup_observer(
      original_process, RenderProcessHostWatcher::WATCH_FOR_HOST_DESTRUCTION);
  EXPECT_TRUE(NavigateToURL(shell(), url_b));
  cleanup_observer.Wait();
  RenderFrameHostImpl* rfh_b = root->current_frame_host();
  EXPECT_NE(original_process_id, rfh_b->GetProcess()->GetID());
  EXPECT_EQ(1, process_exits_);
  EXPECT_EQ(1, host_destructions_);

  // During the cross-process navigation, we should not have used the leak
  // cleanup approach. The leak cleanup would set FastShutdownStarted.
  EXPECT_EQ(0, fast_shutdown_observer.fast_shutdown_exit_count());
  EXPECT_EQ(0, leak_cleanup_observer.termination_count());
}

// Similar to AllowUnusedProcessToExit, for the case that a sad frame from a
// previous renderer crash is the only remaining RenderFrameHost in a process.
// See https://crbug.com/1226834.
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest,
                       AllowUnusedProcessToExitAfterCrash) {
  ASSERT_TRUE(embedded_test_server()->Start());

  // Ensure all sites get dedicated processes during the test.
  IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());

  GURL initial_url(embedded_test_server()->GetURL(
      "a.com", "/cross_site_iframe_factory.html?a(b,b)"));
  EXPECT_TRUE(NavigateToURL(shell(), initial_url));
  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
                            ->GetPrimaryFrameTree()
                            .root();
  RenderFrameHostImpl* child_rfh0 = root->child_at(0)->current_frame_host();
  RenderFrameHostImpl* child_rfh1 = root->child_at(1)->current_frame_host();
  RenderViewHostImpl* rvh_b = child_rfh0->render_view_host();
  int process_b_id = child_rfh0->GetProcess()->GetID();
  EXPECT_EQ(child_rfh0->GetProcess(), child_rfh1->GetProcess());
  EXPECT_TRUE(child_rfh0->GetProcess()->IsInitializedAndNotDead());
  EXPECT_TRUE(child_rfh0->IsRenderFrameLive());
  EXPECT_TRUE(child_rfh1->IsRenderFrameLive());
  EXPECT_TRUE(rvh_b->IsRenderViewLive());

  // Reset the process exit related counts.
  process_exits_ = 0;
  host_destructions_ = 0;
  Observe(child_rfh0->GetProcess());

  // Terminate the subframe process.
  {
    RenderProcessHostWatcher termination_observer(
        child_rfh0->GetProcess(),
        RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
    child_rfh0->GetProcess()->Shutdown(0);
    termination_observer.Wait();
  }
  EXPECT_FALSE(child_rfh0->GetProcess()->IsInitializedAndNotDead());
  EXPECT_FALSE(child_rfh0->IsRenderFrameLive());
  EXPECT_FALSE(child_rfh1->IsRenderFrameLive());
  EXPECT_FALSE(rvh_b->IsRenderViewLive());
  EXPECT_EQ(1, process_exits_);
  EXPECT_EQ(0, host_destructions_);

  // Reset the process exit related counts.
  process_exits_ = 0;
  host_destructions_ = 0;

  // Reload the first frame but not the second. This will replace child_rfh0
  // with a new RFH in the same SiteInstance and process as before.
  {
    TestFrameNavigationObserver reload_observer(root->child_at(0));
    std::string reload_script(
        "var f = document.getElementById('child-0');"
        "f.src = f.src;");
    EXPECT_TRUE(ExecuteScript(root, reload_script));
    reload_observer.Wait();
  }
  RenderFrameHostImpl* new_child_rfh0 = root->child_at(0)->current_frame_host();
  EXPECT_EQ(child_rfh1->GetProcess(), new_child_rfh0->GetProcess());
  EXPECT_TRUE(new_child_rfh0->GetProcess()->IsInitializedAndNotDead());
  EXPECT_TRUE(new_child_rfh0->IsRenderFrameLive());
  EXPECT_FALSE(child_rfh1->IsRenderFrameLive());

  // Navigate the first frame to a different process. This again replaces
  // new_child_rfh0 with a new RFH, now in a different process.
  GURL url_c(embedded_test_server()->GetURL("c.com", "/title1.html"));
  RenderFrameDeletedObserver rfh_deleted_observer(new_child_rfh0);
  TestFrameNavigationObserver navigation_observer(root->child_at(0));
  EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), url_c));
  navigation_observer.Wait();
  rfh_deleted_observer.WaitUntilDeleted();
  EXPECT_NE(child_rfh1->GetProcess(),
            root->child_at(0)->current_frame_host()->GetProcess());

  // This used to leave the b.com process running, since child_rfh1 was still
  // counted as an active frame in the RenderProcessHost even though it wasn't
  // live.
  EXPECT_FALSE(child_rfh1->GetProcess()->IsInitializedAndNotDead());
  EXPECT_FALSE(child_rfh1->IsRenderFrameLive());
  EXPECT_FALSE(rvh_b->IsRenderViewLive());
  EXPECT_EQ(1, process_exits_);
  EXPECT_EQ(0, host_destructions_);

  // There shouldn't be live proxies either.
  RenderFrameProxyHost* proxy =
      root->current_frame_host()
          ->browsing_context_state()
          ->GetRenderFrameProxyHost(child_rfh1->GetSiteInstance()->group());
  EXPECT_FALSE(proxy->is_render_frame_proxy_live());

  // Reset the process exit related counts.
  process_exits_ = 0;
  host_destructions_ = 0;

  // After the leak cleanup, reload the second frame to make sure it still works
  // in the original RenderProcessHost, even though it swaps to a new
  // RenderFrameHost.
  {
    TestFrameNavigationObserver reload_observer(root->child_at(1));
    std::string reload_script(
        "var f = document.getElementById('child-1');"
        "f.src = f.src;");
    EXPECT_TRUE(ExecuteScript(root, reload_script));
    reload_observer.Wait();
  }
  RenderFrameHostImpl* new_child_rfh1 = root->child_at(1)->current_frame_host();
  EXPECT_EQ(process_b_id, new_child_rfh1->GetProcess()->GetID());
  EXPECT_TRUE(new_child_rfh1->GetProcess()->IsInitializedAndNotDead());
  EXPECT_TRUE(new_child_rfh1->IsRenderFrameLive());
  EXPECT_EQ(0, process_exits_);
  EXPECT_EQ(0, host_destructions_);
}

// Test that RenderProcessHostImpl::Cleanup can handle nested deletions of
// RenderFrameHost objects, when we might encounter a parent RFH that is tracked
// among the IPC listeners but is no longer discoverable via FromID, while
// handling the deletion of a subframe. One way this can occur is during bfcache
// eviction.
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, HandleNestedFrameDeletion) {
  ASSERT_TRUE(embedded_test_server()->Start());

  // Ensure all sites get dedicated processes during the test.
  IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());

  // Navigate to a page with a same-process subframe.
  GURL url_a(embedded_test_server()->GetURL("a.com", "/page_with_iframe.html"));
  EXPECT_TRUE(NavigateToURL(shell(), url_a));
  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
                            ->GetPrimaryFrameTree()
                            .root();
  RenderFrameHostImpl* rfh_a = root->current_frame_host();
  RenderProcessHost* process_a = rfh_a->GetProcess();
  int process_a_id = process_a->GetID();
  Observe(process_a);

  // Navigate cross-process and evict process A from the back-forward cache.
  // This should not cause a crash when looking for non-live RenderFrameHosts.
  GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
  RenderProcessHostWatcher cleanup_observer(
      process_a, RenderProcessHostWatcher::WATCH_FOR_HOST_DESTRUCTION);
  EXPECT_TRUE(NavigateToURL(shell(), url_b));
  shell()->web_contents()->GetController().GetBackForwardCache().Flush();
  cleanup_observer.Wait();
  RenderFrameHostImpl* rfh_b = root->current_frame_host();
  EXPECT_NE(process_a_id, rfh_b->GetProcess()->GetID());
  EXPECT_EQ(1, process_exits_);
  EXPECT_EQ(1, host_destructions_);
}

namespace {

// Observer that listens for RenderFrameDeleted and iterates over the remaining
// RenderFrameHosts in the process at the time. This catches a case where a
// parent RenderFrameHost might not be found via RenderFrameHost::FromID because
// of nested frame deletion, which used to cause a CHECK failure.
class RenderFrameDeletionObserver : public WebContentsObserver {
 public:
  explicit RenderFrameDeletionObserver(WebContents* web_contents)
      : WebContentsObserver(web_contents) {}
  ~RenderFrameDeletionObserver() override = default;

  // WebContentsObserver:
  void RenderFrameDeleted(RenderFrameHost* render_frame_host) override {
    render_frame_deleted_count_++;

    // Find all other RenderFrameHosts in the process, which should exclude the
    // one being deleted.
    auto rfh_collector = [](std::vector<RenderFrameHost*>& results,
                            RenderFrameHost* rfh) { results.push_back(rfh); };
    RenderProcessHostImpl* process =
        static_cast<RenderProcessHostImpl*>(render_frame_host->GetProcess());
    std::vector<RenderFrameHost*> all_rfhs;
    process->ForEachRenderFrameHost(
        base::BindRepeating(rfh_collector, std::ref(all_rfhs)));

    // Update the cumulative count of other RenderFrameHosts in the process.
    render_frame_host_iterator_count_ += all_rfhs.size();
  }

  // Returns how many time RenderFrameDeleted was called.
  int render_frame_deleted_count() { return render_frame_deleted_count_; }

  // Returns a cumulative count of how many remaining RenderFrameHosts were
  // found in the process at the time of any RenderFrameDeleted calls.
  int render_frame_host_iterator_count() {
    return render_frame_host_iterator_count_;
  }

 private:
  int render_frame_deleted_count_ = 0;
  int render_frame_host_iterator_count_ = 0;
};

}  // namespace

// Test that RenderProcessHost::ForEachRenderFrameHost can handle nested
// deletions of RenderFrameHost objects, when we might encounter a parent RFH
// that is no longer discoverable via FromID, while handling the deletion of a
// subframe. One way this can occur is during bfcache eviction.
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, ForEachFrameNestedFrameDeletion) {
  // This test specifically wants to test with BackForwardCache eviction, so
  // skip it if BackForwardCache is disabled.
  if (!IsBackForwardCacheEnabled())
    return;
  ASSERT_TRUE(embedded_test_server()->Start());

  // Ensure all sites get dedicated processes during the test.
  IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());

  // Navigate to a page with a same-process subframe.
  GURL url_a(embedded_test_server()->GetURL("a.com", "/page_with_iframe.html"));
  EXPECT_TRUE(NavigateToURL(shell(), url_a));
  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
                            ->GetPrimaryFrameTree()
                            .root();
  RenderFrameHostImpl* rfh_a = root->current_frame_host();
  RenderProcessHost* process_a = rfh_a->GetProcess();
  int process_a_id = process_a->GetID();

  // Listen for RenderFrameDeleted and count the other RenderFrameHosts in the
  // process at the time.
  RenderFrameDeletionObserver rfh_deletion_observer(shell()->web_contents());

  // Navigate cross-process and evict process A from the back-forward cache.
  // This should not cause a crash when iterating over RenderFrameHosts.
  GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
  RenderProcessHostWatcher cleanup_observer(
      process_a, RenderProcessHostWatcher::WATCH_FOR_HOST_DESTRUCTION);
  EXPECT_TRUE(NavigateToURL(shell(), url_b));
  shell()->web_contents()->GetController().GetBackForwardCache().Flush();
  cleanup_observer.Wait();
  RenderFrameHostImpl* rfh_b = root->current_frame_host();
  EXPECT_NE(process_a_id, rfh_b->GetProcess()->GetID());

  // RenderFrameDeleted should have been called for both the main frame and
  // subframe in process A.
  EXPECT_EQ(2, rfh_deletion_observer.render_frame_deleted_count());

  // The subframe's RenderFrameDeleted happens after both the main frame and
  // subframe have become undiscoverable by FromID (i.e., removed from
  // g_routing_id_frame_map). The subframe isn't expected to be found during its
  // own RenderFrameDeleted (since it has been removed from
  // render_frame_host_id_set_), but the partially destructed main frame also
  // isn't found at that time when iterating over other frames in the process.
  EXPECT_EQ(0, rfh_deletion_observer.render_frame_host_iterator_count());
}

#if BUILDFLAG(IS_WIN)
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, ZeroExecutionTimes) {
  // This test only works if the renderer process is sandboxed.
  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
          sandbox::policy::switches::kNoSandbox)) {
    return;
  }
  base::HistogramTester histogram_tester;
  RenderProcessHost* process = RenderProcessHostImpl::CreateRenderProcessHost(
      ShellContentBrowserClient::Get()->browser_context(), nullptr);
  RenderProcessHostWatcher process_watcher(
      process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_READY);
  process->Init();
  process_watcher.Wait();
  EXPECT_TRUE(process->IsReady());
  histogram_tester.ExpectUniqueSample(
      "BrowserRenderProcessHost.SuspendedChild.UserExecutionRecorded", false,
      1);
  histogram_tester.ExpectUniqueSample(
      "BrowserRenderProcessHost.SuspendedChild.KernelExecutionRecorded", false,
      1);
  process->Cleanup();
}

class RenderProcessHostWriteableFileTest
    : public RenderProcessHostTestBase,
      public ::testing::WithParamInterface<
          std::tuple</*enforcement_enabled=*/bool,
                     /*add_no_execute_flags=*/bool>> {
 public:
  void SetUp() override {
    enforcement_feature_.InitWithFeatureState(
        base::features::kEnforceNoExecutableFileHandles,
        IsEnforcementEnabled());
    RenderProcessHostTestBase::SetUp();
  }

 protected:
  bool IsEnforcementEnabled() { return std::get<0>(GetParam()); }
  bool ShouldMarkNoExecute() { return std::get<1>(GetParam()); }

 private:
  base::test::ScopedFeatureList enforcement_feature_;
};

// This test verifies that the renderer process is wired up correctly with the
// mojo invitation flag that indicates that it's untrusted. The other half of
// this test that verifies that a security violation actually causes a DCHECK
// lives in mojo/core, and can't live here as death tests are not supported for
// browser tests.
IN_PROC_BROWSER_TEST_P(RenderProcessHostWriteableFileTest,
                       PassUnsafeWriteableExecutableFile) {
  // This test only works if DCHECKs are enabled.
#if !DCHECK_IS_ON()
  GTEST_SKIP();
#else
  // This test only works if the renderer process is sandboxed.
  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
          sandbox::policy::switches::kNoSandbox)) {
    GTEST_SKIP();
  }

  base::ScopedAllowBlockingForTesting allow_blocking;

  ASSERT_TRUE(embedded_test_server()->Start());
  GURL test_url = embedded_test_server()->GetURL("/simple_page.html");
  EXPECT_TRUE(NavigateToURL(shell(), test_url));
  RenderProcessHost* rph =
      shell()->web_contents()->GetPrimaryMainFrame()->GetProcess();

  mojo::Remote<mojom::TestService> test_service;
  rph->BindReceiver(test_service.BindNewPipeAndPassReceiver());

  uint32_t flags = base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_READ |
                   base::File::FLAG_WRITE;
  if (ShouldMarkNoExecute()) {
    flags = base::File::AddFlagsForPassingToUntrustedProcess(flags);
  }

  base::FilePath file_path;
  base::CreateTemporaryFile(&file_path);
  base::File temp_file_writeable(file_path, flags);
  ASSERT_TRUE(temp_file_writeable.IsValid());

  bool error_was_called = false;
  mojo::SetUnsafeFileHandleCallbackForTesting(
      base::BindLambdaForTesting([&error_was_called]() -> bool {
        error_was_called = true;
        return true;
      }));

  base::RunLoop run_loop;
  test_service->PassWriteableFile(std::move(temp_file_writeable),
                                  run_loop.QuitClosure());
  run_loop.Run();

  // This test should only detect a violation if enforcement is enabled and the
  // file has not been marked no-execute correctly.
  bool should_violation_occur =
      IsEnforcementEnabled() && !ShouldMarkNoExecute();
  EXPECT_EQ(should_violation_occur, error_was_called);
#endif  // DCHECK_IS_ON()
}

INSTANTIATE_TEST_SUITE_P(
    All,
    RenderProcessHostWriteableFileTest,
    testing::Combine(/*enforcement_enabled=*/testing::Bool(),
                     /*add_no_execute_flags=*/testing::Bool()));

#endif  // BUILDFLAG(IS_WIN)

// This test verifies that the Pseudonymization salt that is generated in the
// browser process is correctly synchronized with a child process, in this case,
// two separate renderer processes.
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest,
                       SetPseudonymizationSaltSynchronized) {
  ASSERT_TRUE(embedded_test_server()->Start());

  // Ensure all sites get dedicated processes during the test.
  IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());

  // Create two renderer processes.
  EXPECT_TRUE(NavigateToURL(
      shell(), embedded_test_server()->GetURL("a.com", "/simple_page.html")));
  RenderProcessHost* rph1 =
      shell()->web_contents()->GetPrimaryMainFrame()->GetProcess();
  Shell* second_shell = CreateBrowser();
  EXPECT_TRUE(NavigateToURL(second_shell, embedded_test_server()->GetURL(
                                              "b.com", "/simple_page.html")));
  RenderProcessHost* rph2 =
      second_shell->web_contents()->GetPrimaryMainFrame()->GetProcess();

  // This test needs two processes.
  EXPECT_NE(rph1->GetProcess().Pid(), rph2->GetProcess().Pid());

  const std::string test_string = "testing123";
  uint32_t browser_result =
      PseudonymizationUtil::PseudonymizeStringForTesting(test_string);

  for (RenderProcessHost* rph : {rph1, rph2}) {
    mojo::Remote<mojom::TestService> service;
    rph->BindReceiver(service.BindNewPipeAndPassReceiver());

    base::RunLoop run_loop;

    absl::optional<uint32_t> renderer_result = absl::nullopt;
    service->PseudonymizeString(
        test_string, base::BindLambdaForTesting([&](uint32_t result) {
          renderer_result = result;
          run_loop.Quit();
        }));
    run_loop.Run();

    ASSERT_TRUE(renderer_result.has_value());
    EXPECT_EQ(*renderer_result, browser_result);
  }
}

class CreationObserver : public RenderProcessHostCreationObserver {
 public:
  explicit CreationObserver(
      base::RepeatingClosure closure = base::RepeatingClosure())
      : closure_(std::move(closure)) {}

  // content::RenderProcessHostCreationObserver:
  void OnRenderProcessHostCreated(RenderProcessHost* process_host) override {
    if (closure_) {
      closure_.Run();
    }
  }

 private:
  base::RepeatingClosure closure_;
};

IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, HostCreationObserved) {
  int created_count = 0;
  CreationObserver creation_observer(
      base::BindLambdaForTesting([&created_count]() { ++created_count; }));
  RenderProcessHost* process = RenderProcessHostImpl::CreateRenderProcessHost(
      ShellContentBrowserClient::Get()->browser_context(), nullptr);
  RenderProcessHostWatcher process_watcher(
      process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_READY);
  process->Init();
  process_watcher.Wait();
  ASSERT_TRUE(process->IsReady());
  EXPECT_EQ(1, created_count);
  process->Cleanup();
}

// Notification of RenderProcessHost creation should not crash if creation
// observers are added during notification of another creation observer.
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest,
                       HostCreationObserversAddedDuringNotification) {
  std::vector<std::unique_ptr<CreationObserver>> added_creation_observers;
  const int kObserversToAdd = 1000;
  int added_observer_notification_count = 0;
  const auto increment_added_observer_notification_count =
      base::BindLambdaForTesting([&added_observer_notification_count]() {
        ++added_observer_notification_count;
      });
  CreationObserver creation_observer1(base::BindLambdaForTesting(
      [&added_creation_observers,
       increment_added_observer_notification_count]() {
        for (int i = 0; i < kObserversToAdd; ++i) {
          added_creation_observers.push_back(std::make_unique<CreationObserver>(
              increment_added_observer_notification_count));
        }
      }));
  CreationObserver creation_observer2;

  RenderProcessHost* process = RenderProcessHostImpl::CreateRenderProcessHost(
      ShellContentBrowserClient::Get()->browser_context(), nullptr);
  RenderProcessHostWatcher process_watcher(
      process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_READY);
  process->Init();
  process_watcher.Wait();
  EXPECT_TRUE(process->IsReady());
  EXPECT_EQ(kObserversToAdd, added_observer_notification_count);
  process->Cleanup();
}

// Notification of RenderProcessHost creation should not crash if a creation
// observer is destroyed during notification of another creation observer.
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest,
                       HostCreationObserversDestroyedDuringNotification) {
  base::OnceClosure destroy_second_observer;
  CreationObserver creation_observer1(
      base::BindLambdaForTesting([&destroy_second_observer]() {
        std::move(destroy_second_observer).Run();
      }));
  auto creation_observer2 = std::make_unique<CreationObserver>();
  destroy_second_observer = base::BindLambdaForTesting(
      [&creation_observer2]() { creation_observer2.reset(); });

  RenderProcessHost* process = RenderProcessHostImpl::CreateRenderProcessHost(
      ShellContentBrowserClient::Get()->browser_context(), nullptr);
  RenderProcessHostWatcher process_watcher(
      process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_READY);
  process->Init();
  process_watcher.Wait();
  EXPECT_TRUE(process->IsReady());
  EXPECT_EQ(nullptr, creation_observer2.get());
  process->Cleanup();
}

}  // namespace content