chromium/content/browser/renderer_host/render_process_host_browsertest.cc

// 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 "content/public/browser/render_process_host.h"

#include <memory>
#include <optional>
#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/mock_callback.h"
#include "base/test/test_timeouts.h"
#include "base/threading/hang_watcher.h"
#include "base/timer/elapsed_timer.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_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/browsing_data_remover.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_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/browsing_data_remover_test_util.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/blink/public/common/chrome_debug_urls.h"
#include "third_party/blink/public/common/features.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

_;
InSequence;
Mock;
StrictMock;
Test;

namespace content {

namespace {

// Similar to net::test_server::DelayedHttpResponse, but the delay is resolved
// through Resolver.
class DelayedHttpResponseWithResolver final
    : public net::test_server::BasicHttpResponse {};

std::unique_ptr<net::test_server::HttpResponse> HandleBeacon(
    const net::test_server::HttpRequest& request) {}

std::unique_ptr<net::test_server::HttpResponse> HandleHungBeacon(
    const base::RepeatingClosure& on_called,
    const net::test_server::HttpRequest& request) {}

std::unique_ptr<net::test_server::HttpResponse> HandleHungBeaconWithResolver(
    scoped_refptr<DelayedHttpResponseWithResolver::Resolver> resolver,
    const net::test_server::HttpRequest& request) {}

}  // namespace

class RenderProcessHostTestBase : public ContentBrowserTest,
                                  public RenderProcessHostObserver {};

// A ContentBrowserClient that can wait for calls to
// `blink::mojom::KeepAliveHandle`.
class KeepAliveHandleContentBrowserClient
    : public ContentBrowserTestContentBrowserClient {};

class RenderProcessHostTest : public RenderProcessHostTestBase,
                              public ::testing::WithParamInterface<bool> {};

INSTANTIATE_TEST_SUITE_P(
    All,
    RenderProcessHostTest,
    testing::Values(false, true),
    [](const testing::TestParamInfo<RenderProcessHostTest::ParamType>& info) {};

IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, GuestsAreNotSuitableHosts) {}

class ShellCloser : public RenderProcessHostObserver {};

class ObserverLogger : public RenderProcessHostObserver {};

// Flaky on Android. http://crbug.com/759514.
#if BUILDFLAG(IS_ANDROID)
#define MAYBE_AllProcessExitedCallsBeforeAnyHostDestroyedCalls
#else
#define MAYBE_AllProcessExitedCallsBeforeAnyHostDestroyedCalls
#endif
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest,
                       MAYBE_AllProcessExitedCallsBeforeAnyHostDestroyedCalls) {}

IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, KillProcessOnBadMojoMessage) {}

// Observes a WebContents and a specific frame within it, and waits until they
// both indicate that they are audible.
class AudioStartObserver : public WebContentsObserver {};

// 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
#endif
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, KillProcessZerosAudioStreams) {}

// Test class instance to run specific setup steps for capture streams.
class CaptureStreamRenderProcessHostTest : public RenderProcessHostTestBase {};

// Tests that video capture stream count increments when getUserMedia() is
// called.
IN_PROC_BROWSER_TEST_F(CaptureStreamRenderProcessHostTest,
                       GetUserMediaIncrementsVideoCaptureStreams) {}

// Tests that video capture stream count resets when getUserMedia() is called
// and stopped.
IN_PROC_BROWSER_TEST_F(CaptureStreamRenderProcessHostTest,
                       StopResetsVideoCaptureStreams) {}

// Tests that video capture stream counts (used for process priority
// calculations) are properly set and cleared during media playback and renderer
// terminations.
// Test is flaky on Android builders: https://crbug.com/352065578
#if BUILDFLAG(IS_ANDROID)
#define MAYBE_KillProcessZerosVideoCaptureStreams
#else
#define MAYBE_KillProcessZerosVideoCaptureStreams
#endif
IN_PROC_BROWSER_TEST_F(CaptureStreamRenderProcessHostTest,
                       MAYBE_KillProcessZerosVideoCaptureStreams) {}

// Tests that media stream count increments when getUserMedia() is
// called with audio only.
IN_PROC_BROWSER_TEST_F(CaptureStreamRenderProcessHostTest,
                       GetUserMediaAudioOnlyIncrementsMediaStreams) {}

// 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.
// Test is flaky on Android builders: https://crbug.com/352065578
#if BUILDFLAG(IS_ANDROID)
#define MAYBE_KillProcessZerosVideoCaptureStreams
#else
#define MAYBE_KillProcessZerosVideoCaptureStreams
#endif
IN_PROC_BROWSER_TEST_F(CaptureStreamRenderProcessHostTest,
                       KillProcessZerosAudioCaptureStreams) {}

IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, KeepAliveRendererProcess) {}

// TODO(crbug.com/40275040): Fix and re-enable.
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest,
                       DISABLED_KeepAliveRendererProcessWithServiceWorker) {}

// Test is flaky on Android builders: https://crbug.com/875179
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_WIN)
#define MAYBE_KeepAliveRendererProcess_Hung
#else
#define MAYBE_KeepAliveRendererProcess_Hung
#endif
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest,
                       MAYBE_KeepAliveRendererProcess_Hung) {}

// Test is flaky on Android builders: https://crbug.com/875179
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_WIN)
#define MAYBE_FetchKeepAliveRendererProcess_Hung
#else
#define MAYBE_FetchKeepAliveRendererProcess_Hung
#endif
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest,
                       MAYBE_FetchKeepAliveRendererProcess_Hung) {}

IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, ManyKeepaliveRequests) {}

IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, TooManyKeepaliveRequests) {}

// 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 {};

#if !BUILDFLAG(IS_ANDROID)
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, PriorityOverride) {}
#endif  // !BUILDFLAG(IS_ANDROID)

struct BoostRenderProcessForLoadingBrowserTestParam {};

// This test verifies `kBoostRenderProcessForLoading` feature can keep the
// RenderProcessHost foregrounded until `DOMContentLoaded` comes.
class BoostRenderProcessForLoadingBrowserTest
    : public RenderProcessHostTestBase,
      public content::WebContentsObserver,
      public ::testing::WithParamInterface<
          BoostRenderProcessForLoadingBrowserTestParam> {};

const BoostRenderProcessForLoadingBrowserTestParam
    kBoostRenderProcessForLoadingBrowserTestParams[] =;

INSTANTIATE_TEST_SUITE_P();

IN_PROC_BROWSER_TEST_P(BoostRenderProcessForLoadingBrowserTest,
                       VerifyRenderProcessBackgrounded) {}

// This test verifies properties of RenderProcessHostImpl *before* Init method
// is called.
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, ConstructedButNotInitializedYet) {}

// This test verifies that a fast shutdown is possible for a starting process.
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, FastShutdownForStartingProcess) {}

// Verifies that a fast shutdown is possible with pending keepalive request.
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest,
                       FastShutdownWithKeepAliveRequest) {}

// 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) {}

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 {};

// Observer that listens for process exits and counts when they are treated as
// fast shutdown cases.
class FastShutdownExitObserver : public RenderProcessHostObserver {};

}  // 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) {}

// 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) {}

// 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) {}

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 {};

}  // 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) {}

#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) {}

class CreationObserver : public RenderProcessHostCreationObserver {};

IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, HostCreationObserved) {}

// 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) {}

// 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) {}

namespace {

bool FetchScript(Shell* shell, GURL url) {}

}  // namespace

// Tests that BrowsingDataRemover clears renderer's in-memory resource cache.
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, ClearResourceCache) {}

// Tests that RenderProcessHost reuse works correctly even if the site URL of a
// URL changes.
IN_PROC_BROWSER_TEST_P(RenderProcessHostTest, ReuseSiteURLChanges) {}

#if BUILDFLAG(ALLOW_OOP_VIDEO_DECODER)
class FakeStableVideoDecoderFactoryService
    : public media::stable::mojom::StableVideoDecoderFactory {};

class RenderProcessHostTestStableVideoDecoderTest
    : public RenderProcessHostTestBase {};

// Ensures that the StableVideoDecoderFactory connection is terminated after a
// delay once all the StableVideoDecoders created with it have disconnected.
IN_PROC_BROWSER_TEST_F(RenderProcessHostTestStableVideoDecoderTest,
                       FactoryIsResetAfterDelay) {}

// Ensures that the timer that destroys the StableVideoDecoderFactory connection
// when all StableVideoDecoder connections die is stopped if a request to
// connect another StableVideoDecoder is received soon enough.
IN_PROC_BROWSER_TEST_F(RenderProcessHostTestStableVideoDecoderTest,
                       FactoryResetTimerIsStoppedOnRequestBeforeResetDelay) {}

#endif  // BUILDFLAG(ALLOW_OOP_VIDEO_DECODER)

}  // namespace content