chromium/content/browser/renderer_host/render_frame_host_impl_unittest.cc

// Copyright 2020 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/browser/renderer_host/render_frame_host_impl.h"

#include <memory>

#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/test/bind.h"
#include "base/test/scoped_feature_list.h"
#include "build/buildflag.h"
#include "components/input/timeout_monitor.h"
#include "content/browser/renderer_host/navigation_controller_impl.h"
#include "content/common/content_navigation_policy.h"
#include "content/common/features.h"
#include "content/public/browser/cors_origin_pattern_setter.h"
#include "content/public/browser/shared_cors_origin_access_list.h"
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/common/content_features.h"
#include "content/public/test/back_forward_cache_util.h"
#include "content/public/test/fake_local_frame.h"
#include "content/public/test/test_utils.h"
#include "content/test/navigation_simulator_impl.h"
#include "content/test/test_render_frame_host.h"
#include "content/test/test_render_view_host.h"
#include "content/test/test_web_contents.h"
#include "net/base/features.h"
#include "net/base/isolation_info.h"
#include "net/cookies/site_for_cookies.h"
#include "services/network/public/cpp/cors/origin_access_list.h"
#include "services/network/public/mojom/cors.mojom.h"
#include "services/network/public/mojom/cors_origin_pattern.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/runtime_feature_state/runtime_feature_state_context.h"
#include "third_party/blink/public/common/runtime_feature_state/runtime_feature_state_read_context.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"
#include "third_party/blink/public/mojom/favicon/favicon_url.mojom.h"
#include "third_party/blink/public/mojom/webauthn/authenticator.mojom.h"
#include "url/gurl.h"
#include "url/origin.h"
#include "url/url_util.h"

#if !BUILDFLAG(IS_ANDROID)
#include "content/browser/webauth/authenticator_environment.h"
#include "content/public/common/content_switches.h"
#include "third_party/blink/public/mojom/webauthn/virtual_authenticator.mojom.h"
#endif

namespace content {

namespace {

void AddHostPermissions(const std::string& host, RenderFrameHost* rfh) {}

}  // namespace

class RenderFrameHostImplTest : public RenderViewHostImplTestHarness {};

// TODO(crbug.com/40260854): This set-up is temporary. Eventually, all
// tests that reference extensions will be moved to chrome/browser/ and this
// class can be deleted.
class FirstPartyOverrideContentBrowserClient : public ContentBrowserClient {};

// A test class that forces kOriginKeyedProcessesByDefault off for tests that
// require that same-site cross-origin navigations don't trigger a RFH swap.
class RenderFrameHostImplTest_NoOriginKeyedProcessesByDefault
    : public RenderFrameHostImplTest {};

// Note: Since this test is predicate on not having a RFH swap for a
// cross-origin, same-site navigation, it only makes sense to run it with
// kOriginKeyedProcessesByDefault disabled.
TEST_F(RenderFrameHostImplTest_NoOriginKeyedProcessesByDefault,
       ExpectedMainWorldOrigin) {}

// Test that navigating to an invalid URL (which creates an empty GURL) causes
// about:blank to commit.
TEST_F(RenderFrameHostImplTest, InvalidURL) {}

// Ensures that IsolationInfo's SiteForCookies is empty and
// that it correctly generates a StorageKey with a kCrossSite
// AncestorChainBit when frames are nested in an A->B->A
// configuration.
TEST_F(RenderFrameHostImplTest, CrossSiteAncestorInFrameTree) {}

// Test the IsolationInfo and related fields of a request during the various
// phases of a commit, when a RenderFrameHost is reused. Once RenderDocument
// ships, this test may no longer be needed.
// Note: Since this test is predicate on not having a RFH swap for a
// cross-origin, same-site navigation, it only makes sense to run it with
// kOriginKeyedProcessesByDefault disabled.
TEST_F(RenderFrameHostImplTest_NoOriginKeyedProcessesByDefault,
       IsolationInfoDuringCommit) {}

TEST_F(RenderFrameHostImplTest, PolicyContainerLifecycle) {}

TEST_F(RenderFrameHostImplTest, FaviconURLsSet) {}

TEST_F(RenderFrameHostImplTest, FaviconURLsResetWithNavigation) {}

TEST_F(RenderFrameHostImplTest, ChildOfCredentiallessIsCredentialless) {}

// FakeLocalFrame implementation that records calls to BeforeUnload().
class FakeLocalFrameWithBeforeUnload : public content::FakeLocalFrame {};

// Verifies BeforeUnload() is not sent to renderer if there is no before
// unload handler present.
TEST_F(RenderFrameHostImplTest, BeforeUnloadNotSentToRenderer) {}

class LoadingStateChangedDelegate : public WebContentsDelegate {};

TEST_F(RenderFrameHostImplTest, NavigationApiInterceptShowLoadingUi) {}

TEST_F(RenderFrameHostImplTest, NavigationApiInterceptBrowserInitiated) {}

// TODO(crbug.com/40260854): This test should be migrated to //chrome.
TEST_F(RenderFrameHostImplTest, CalculateStorageKey) {}

// TODO(crbug.com/41483148): Flaky on Linux.
#if BUILDFLAG(IS_LINUX)
#define MAYBE_CalculateStorageKeyFirstPartyOverride
#else
#define MAYBE_CalculateStorageKeyFirstPartyOverride
#endif

// TODO(crbug.com/40260854): Eventually, this test will be moved to
// chrome/browser/ so that we no longer need to override the
// ContentBrowserClient, and we can test using real extension URLs.
TEST_F(RenderFrameHostImplTest, MAYBE_CalculateStorageKeyFirstPartyOverride) {}

TEST_F(RenderFrameHostImplTest,
       CalculateStorageKeyWhenPassedOriginIsNotCurrentFrame) {}

// Test that the correct StorageKey is calculated when a RFH takes its document
// properties from a navigation.
// TODO(crbug.com/40092527): Once we are able to compute the origin to
// commit in the browser, `navigation_request->commit_params().storage_key`
// will contain the correct origin and it won't be necessary to override it
// with `param.origin` anymore. Meaning this test may be removed because we
// already check that the NavigationRequest calculates the correct key.
TEST_F(RenderFrameHostImplTest,
       CalculateStorageKeyTakeNewDocumentPropertiesFromNavigation) {}

// Tests that the StorageKey calculated for a frame under an extension main
// frame has storage partitioning enabled/disabled as expected via the
// RuntimeFeatureStateReadContext when the extension has host permissions.
// TODO(crbug.com/40260854): This test should be migrated to //chrome.
TEST_F(RenderFrameHostImplTest,
       CalculateStorageKeyStoragePartitioningCorrectFrameWithExtension) {}

// Test that CalculateStorageKey creates a first-party or third-party key
// depending on state of Storage Partitioning the main frame's
// RuntimeFeatureStateReadContext for a new unnavigated frame.
TEST_F(RenderFrameHostImplTest, CalculateStorageKeyOfUnnavigatedFrame) {}

TEST_F(RenderFrameHostImplTest,
       NewFrameInheritsRuntimeFeatureStateReadContext) {}

#if BUILDFLAG(IS_ANDROID)
class TestWebAuthnContentBrowserClientImpl : public ContentBrowserClient {
 public:
  MOCK_METHOD(bool,
              IsSecurityLevelAcceptableForWebAuthn,
              (RenderFrameHost*, const url::Origin& origin),
              ());
};

class RenderFrameHostImplWebAuthnTest : public RenderFrameHostImplTest {
 public:
  void SetUp() override {
    RenderFrameHostImplTest::SetUp();
    old_browser_client_ = SetBrowserClientForTesting(browser_client_.get());
    contents()->GetController().LoadURLWithParams(
        NavigationController::LoadURLParams(
            GURL("https://example.com/navigation.html")));
  }

  void TearDown() override {
    RenderFrameHostImplTest::TearDown();
    SetBrowserClientForTesting(old_browser_client_);
  }

 protected:
  raw_ptr<ContentBrowserClient> old_browser_client_;
  std::unique_ptr<TestWebAuthnContentBrowserClientImpl> browser_client_ =
      std::make_unique<TestWebAuthnContentBrowserClientImpl>();
};

TEST_F(RenderFrameHostImplWebAuthnTest,
       PerformGetAssertionWebAuthSecurityChecks_TLSError) {
  GURL url("https://doofenshmirtz.evil");
  const auto origin = url::Origin::Create(url);
  EXPECT_CALL(*browser_client_,
              IsSecurityLevelAcceptableForWebAuthn(main_test_rfh(), origin))
      .WillOnce(testing::Return(false));
  std::optional<blink::mojom::AuthenticatorStatus> status;
  main_test_rfh()->PerformGetAssertionWebAuthSecurityChecks(
      "doofenshmirtz.evil", url::Origin::Create(url),
      /*is_payment_credential_get_assertion=*/false,
      base::BindLambdaForTesting(
          [&status](blink::mojom::AuthenticatorStatus s, bool is_cross_origin) {
            status = s;
          }));
  EXPECT_EQ(status.value(),
            blink::mojom::AuthenticatorStatus::CERTIFICATE_ERROR);
}

TEST_F(RenderFrameHostImplWebAuthnTest,
       PerformMakeCredentialWebAuthSecurityChecks_TLSError) {
  GURL url("https://doofenshmirtz.evil");
  const auto origin = url::Origin::Create(url);
  EXPECT_CALL(*browser_client_,
              IsSecurityLevelAcceptableForWebAuthn(main_test_rfh(), origin))
      .WillOnce(testing::Return(false));
  std::optional<blink::mojom::AuthenticatorStatus> status;
  main_test_rfh()->PerformMakeCredentialWebAuthSecurityChecks(
      "doofenshmirtz.evil", url::Origin::Create(url),
      /*is_payment_credential_creation=*/false,
      base::BindLambdaForTesting(
          [&status](blink::mojom::AuthenticatorStatus s, bool is_cross_origin) {
            status = s;
          }));
  EXPECT_EQ(status.value(),
            blink::mojom::AuthenticatorStatus::CERTIFICATE_ERROR);
}

TEST_F(RenderFrameHostImplWebAuthnTest,
       PerformGetAssertionWebAuthSecurityChecks_Success) {
  GURL url("https://owca.org");
  const auto origin = url::Origin::Create(url);
  EXPECT_CALL(*browser_client_,
              IsSecurityLevelAcceptableForWebAuthn(main_test_rfh(), origin))
      .WillOnce(testing::Return(true));
  std::optional<blink::mojom::AuthenticatorStatus> status;
  main_test_rfh()->PerformGetAssertionWebAuthSecurityChecks(
      "owca.org", url::Origin::Create(url),
      /*is_payment_credential_get_assertion=*/false,
      base::BindLambdaForTesting(
          [&status](blink::mojom::AuthenticatorStatus s, bool is_cross_origin) {
            status = s;
          }));
  EXPECT_EQ(status.value(), blink::mojom::AuthenticatorStatus::SUCCESS);
}

TEST_F(RenderFrameHostImplWebAuthnTest,
       PerformMakeCredentialWebAuthSecurityChecks_Success) {
  GURL url("https://owca.org");
  const auto origin = url::Origin::Create(url);
  EXPECT_CALL(*browser_client_,
              IsSecurityLevelAcceptableForWebAuthn(main_test_rfh(), origin))
      .WillOnce(testing::Return(true));
  std::optional<blink::mojom::AuthenticatorStatus> status;
  main_test_rfh()->PerformMakeCredentialWebAuthSecurityChecks(
      "owca.org", url::Origin::Create(url),
      /*is_payment_credential_creation=*/false,
      base::BindLambdaForTesting(
          [&status](blink::mojom::AuthenticatorStatus s, bool is_cross_origin) {
            status = s;
          }));
  EXPECT_EQ(status.value(), blink::mojom::AuthenticatorStatus::SUCCESS);
}

#endif  // BUILDFLAG(IS_ANDROID)

class RenderFrameHostImplThirdPartyStorageTest
    : public RenderViewHostImplTestHarness,
      public testing::WithParamInterface<bool> {};

INSTANTIATE_TEST_SUITE_P();

TEST_P(RenderFrameHostImplThirdPartyStorageTest,
       ChildFramePartitionedByThirdPartyStorageKey) {}

#if !BUILDFLAG(IS_ANDROID)
TEST_F(RenderFrameHostImplTest, GetVirtualAuthenticatorManagerWhenInactiveRFH) {}
#endif

namespace {

class MockWebContentsDelegate : public WebContentsDelegate {};

}  // namespace

// Ensure that a close request from the renderer process is ignored if a
// navigation causes a different RenderFrameHost to commit first. See
// https://crbug.com/1406023.
TEST_F(RenderFrameHostImplTest,
       RendererInitiatedCloseIsCancelledIfPageIsntPrimary) {}

// Ensure that a close request from the browser process cannot be ignored even
// if a navigation causes a different RenderFrameHost to commit first. See
// https://crbug.com/1406023.
TEST_F(RenderFrameHostImplTest,
       BrowserInitiatedCloseIsNotCancelledIfPageIsntPrimary) {}

// A mock WebContentsObserver for listening to text copy events.
class TextCopiedEventObserver : public WebContentsObserver {};

// Test that the WebContentObserver is notified when text is copied to the
// clipboard for a RenderFrameHost.
TEST_F(RenderFrameHostImplTest, OnTextCopiedToClipboard) {}

// Test if `LoadedWithCacheControlNoStoreHeader()` behaves
// as expected.
TEST_F(RenderFrameHostImplTest, LoadedWithCacheControlNoStoreHeader) {}

class MediaStreamCaptureObserver : public WebContentsObserver {};

TEST_F(RenderFrameHostImplTest, CapturedMediaStreamAddedRemoved) {}

}  // namespace content