chromium/content/renderer/render_frame_impl_browsertest.cc

// Copyright 2015 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/renderer/render_frame_impl.h"

#include <stdint.h>

#include <optional>
#include <tuple>
#include <utility>

#include "base/check_deref.h"
#include "base/command_line.h"
#include "base/debug/leak_annotations.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/protected_memory_buildflags.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind.h"
#include "base/test/gtest_util.h"
#include "base/test/scoped_feature_list.h"
#include "base/unguessable_token.h"
#include "build/build_config.h"
#include "content/common/features.h"
#include "content/common/renderer.mojom.h"
#include "content/public/common/bindings_policy.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/extra_mojo_js_features.mojom.h"
#include "content/public/renderer/content_renderer_client.h"
#include "content/public/test/frame_load_waiter.h"
#include "content/public/test/local_frame_host_interceptor.h"
#include "content/public/test/policy_container_utils.h"
#include "content/public/test/render_view_test.h"
#include "content/public/test/test_utils.h"
#include "content/renderer/agent_scheduling_group.h"
#include "content/renderer/document_state.h"
#include "content/renderer/mojo/blink_interface_registry_impl.h"
#include "content/renderer/navigation_state.h"
#include "content/test/frame_host_test_interface.mojom.h"
#include "content/test/test_render_frame.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/navigation/navigation_params.h"
#include "third_party/blink/public/common/navigation/navigation_params_mojom_traits.h"
#include "third_party/blink/public/common/tokens/tokens.h"
#include "third_party/blink/public/mojom/browser_interface_broker.mojom-forward.h"
#include "third_party/blink/public/mojom/frame/frame_owner_properties.mojom.h"
#include "third_party/blink/public/mojom/frame/frame_replication_state.mojom.h"
#include "third_party/blink/public/mojom/frame/tree_scope_type.mojom.h"
#include "third_party/blink/public/mojom/frame/viewport_intersection_state.mojom.h"
#include "third_party/blink/public/mojom/widget/platform_widget.mojom.h"
#include "third_party/blink/public/mojom/widget/record_content_to_visible_time_request.mojom.h"
#include "third_party/blink/public/platform/web_runtime_features.h"
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/public/platform/web_url.h"
#include "third_party/blink/public/platform/web_url_request.h"
#include "third_party/blink/public/platform/web_v8_value_converter.h"
#include "third_party/blink/public/test/test_web_frame_content_dumper.h"
#include "third_party/blink/public/web/web_frame_widget.h"
#include "third_party/blink/public/web/web_history_item.h"
#include "third_party/blink/public/web/web_local_frame.h"
#include "third_party/blink/public/web/web_v8_features.h"
#include "third_party/blink/public/web/web_view.h"
#include "ui/display/screen_info.h"
#include "ui/display/screen_infos.h"
#include "ui/gfx/geometry/point.h"
#include "ui/native_theme/native_theme_features.h"

WebURLRequest;

namespace content {

namespace {

constexpr int32_t kSubframeRouteId =;
constexpr int32_t kSubframeWidgetRouteId =;

const char kParentFrameHTML[] =;
const char kSimpleScriptHtml[] =;

const char kAutoplayTestOrigin[] =;

}  // namespace

// RenderFrameImplTest creates a RenderFrameImpl that is a child of the
// main frame, and has its own RenderWidget. This behaves like an out
// of process frame even though it is in the same process as its parent.
class RenderFrameImplTest : public RenderViewTest {};

class RenderFrameTestObserver : public RenderFrameObserver {};

// Verify that a frame with a WebRemoteFrame as a parent has its own
// RenderWidget.
TEST_F(RenderFrameImplTest, SubframeWidget) {}

// Verify a subframe RenderWidget properly processes its viewport being
// resized.
TEST_F(RenderFrameImplTest, FrameResize) {}

// Verify a subframe RenderWidget properly processes a WasShown message.
TEST_F(RenderFrameImplTest, FrameWasShown) {}

namespace {
class DownloadURLMockLocalFrameHost : public LocalFrameHostInterceptor {};

class DownloadURLTestRenderFrame : public TestRenderFrame {};
}  // namespace

class RenderViewImplDownloadURLTest : public RenderFrameImplTest {};

// Tests that url download are throttled when reaching the limit.
TEST_F(RenderViewImplDownloadURLTest, DownloadUrlLimit) {}

// Regression test for crbug.com/692557. It shouldn't crash if we inititate a
// text finding, and then delete the frame immediately before the text finding
// returns any text match.
TEST_F(RenderFrameImplTest, NoCrashWhenDeletingFrameDuringFind) {}

TEST_F(RenderFrameImplTest, NoCrashOnReceiveTitleWhenNavigatingToJavascript) {}

TEST_F(RenderFrameImplTest, AutoplayFlags) {}

TEST_F(RenderFrameImplTest, AutoplayFlags_WrongOrigin) {}

TEST_F(RenderFrameImplTest, FileUrlPathAlias) {}

TEST_F(RenderFrameImplTest, MainFrameIntersectionRecorded) {}

TEST_F(RenderFrameImplTest, MainFrameViewportRectRecorded) {}

// Used to annotate the source of an interface request.
struct SourceAnnotation {};

std::ostream& operator<<(std::ostream& out, const SourceAnnotation& s) {}

// RenderFrameRemoteInterfacesTest ------------------------------------

namespace {

constexpr char kTestFirstURL[] =;
constexpr char kTestSecondURL[] =;
// constexpr char kTestCrossOriginURL[] = "http://bar.com/";
constexpr char kAboutBlankURL[] =;

constexpr char kFrameEventDidCreateNewFrame[] =;
constexpr char kFrameEventDidCreateNewDocument[] =;
constexpr char kFrameEventDidCreateDocumentElement[] =;
constexpr char kFrameEventReadyToCommitNavigation[] =;
constexpr char kFrameEventDidCommitProvisionalLoad[] =;
constexpr char kFrameEventDidCommitSameDocumentLoad[] =;
constexpr char kFrameEventAfterCommit[] =;

constexpr char kNoDocumentMarkerURL[] =;

class TestSimpleBrowserInterfaceBrokerImpl
    : public blink::mojom::BrowserInterfaceBroker {};

class FrameHostTestInterfaceImpl : public mojom::FrameHostTestInterface {};

// RenderFrameObserver that issues FrameHostTestInterface interface requests
// through the RenderFrame's |remote_interfaces_| in response to observing
// important milestones in a frame's lifecycle.
class FrameHostTestInterfaceRequestIssuer : public RenderFrameObserver {};

// RenderFrameObserver that can be used to wait for the next commit in a frame.
class FrameCommitWaiter : public RenderFrameObserver {};

// Testing ContentRendererClient implementation that fires the |callback|
// whenever a new frame is created.
class FrameCreationObservingRendererClient : public ContentRendererClient {};

// Expects observing the creation of a new frame, and creates an instance of
// FrameHostTestInterfaceRequestIssuerRenderFrame for that new frame to exercise
// its RemoteInterfaceProvider interface.
class ScopedNewFrameInterfaceProviderExerciser {};

// Extracts all interface receivers for FrameHostTestInterface pending on the
// specified |browser_interface_broker_receiver|, and returns a list of the
// source annotations that are provided in the pending Ping() call for each of
// these FrameHostTestInterface receivers.
void ExpectPendingInterfaceReceiversFromSources(
    mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
        browser_interface_broker_receiver,
    std::vector<SourceAnnotation> expected_sources) {}

}  // namespace

class RenderFrameRemoteInterfacesTest : public RenderViewTest {};

// Expect that |remote_interfaces_| is bound before the first committed load in
// a child frame, and then re-bound on the first commit.
TEST_F(RenderFrameRemoteInterfacesTest, ChildFrameAtFirstCommittedLoad) {}

// Expect that |remote_interfaces_| is bound before the first committed load in
// the main frame of an opened window, and then re-bound on the first commit.
TEST_F(RenderFrameRemoteInterfacesTest,
       MainFrameOfOpenedWindowAtFirstCommittedLoad) {}

// Expect that |remote_interfaces_| is not bound to a new pipe if the first
// committed load in the child frame has the same security origin as that of the
// initial empty document.
//
// In this case, the LocalDOMWindow object associated with the initial empty
// document will be re-used for the newly committed document. Here, we must
// continue using the InterfaceProvider connection created for the initial empty
// document to support the following use-case:
//  1) Parent frame dynamically injects an <iframe>.
//  2) The parent frame calls `child.contentDocument.write(...)` to inject
//     Javascript that may stash objects on the child frame's global object
//     (LocalDOMWindow). Internally, these objects may be using Mojo services
//     exposed by the RenderFrameHost. The InterfaceRequests for these services
//     might still be en-route to the RemnderFrameHost's InterfaceProvider.
//  3) The `child` frame commits the first real load, and it is same-origin.
//  4) The global object in the child frame's browsing context is re-used.
//  5) Javascript objects stashed on the global object should continue to work.
//
// TODO(japhet?): javascript: urls are untestable here, because they don't
// go through the normal commit pipeline. If we were to give javascript: urls
// their own DocumentLoader in blink and model them as a real navigation, we
// should add a test case here.
// TODO(crbug.com/40519010): when all clients are converted to use
// BrowserInterfaceBroker, PendingReceiver<InterfaceProvider>-related code will
// be removed.
TEST_F(RenderFrameRemoteInterfacesTest,
       ChildFrameReusingWindowOfInitialDocument) {}

// Expect that |remote_interfaces_| is bound to a new pipe on cross-document
// navigations.
TEST_F(RenderFrameRemoteInterfacesTest, ReplacedOnNonSameDocumentNavigation) {}

// Expect that |remote_interfaces_| is not bound to a new pipe on same-document
// navigations, i.e. the existing InterfaceProvider connection is continued to
// be used.
TEST_F(RenderFrameRemoteInterfacesTest, ReusedOnSameDocumentNavigation) {}

TEST_F(RenderFrameImplTest, LastCommittedUrlForUKM) {}

// Verify that a frame with a pending update is cancelled when a forced update
// is sent.
TEST_F(RenderFrameImplTest, SendUpdateCancelsPending) {}

namespace {

// All content setting tests use the same data url, which contains html which
// has different behavior depending on whether script is enabled or disabled.
blink::mojom::CommonNavigationParamsPtr
GetCommonParamsForContentSettingsTest() {}

// Dump the layout tree and see whether it contains "text".
bool HasText(blink::WebLocalFrame* frame, const std::string& text) {}

// Waits for the navigation to finish.
void NavigateAndWait(content::TestRenderFrame* frame,
                     blink::mojom::CommonNavigationParamsPtr common_params,
                     blink::mojom::CommitNavigationParamsPtr commit_params,
                     blink::WebView* web_view) {}

class FakeContentSettingsClient : public blink::WebContentSettingsClient {};

}  // namespace

// Checks that when images are blocked, the ContentSettingsAgent receives a
// callback.
TEST_F(RenderFrameImplTest, ContentSettingsCallbackImageBlocked) {}

// Checks that when script is blocked, the ContentSettingsAgent receives a
// callback.
TEST_F(RenderFrameImplTest, ContentSettingsCallbackScriptBlocked) {}

// Checks that when script is allowed, the ContentSettingsAgent does not receive
// a callback.
TEST_F(RenderFrameImplTest, ContentSettingsCallbackScriptAllowed) {}

// Regression test for crbug.com/232410: Load a page with JS blocked. Then,
// allow JS and reload the page. In each case, only one of noscript or script
// tags should be enabled, but never both.
TEST_F(RenderFrameImplTest, ContentSettingsNoscriptTag) {}

// Checks that same document navigations don't update content settings for the
// page.
TEST_F(RenderFrameImplTest, ContentSettingsSameDocumentNavigation) {}

class RenderFrameImplMojoJsTest : public RenderViewTest {};

// Verifies enabling MojoJS bindings.
TEST_F(RenderFrameImplMojoJsTest, AllowMojoWebUIBindings) {}

// Verifies enabling MojoJS bindings via EnableMojoJsBindings method.
TEST_F(RenderFrameImplMojoJsTest, EnableMojoJSBindings) {}

// Verifies enabling MojoJS bindings via directly enabling mojo
TEST_F(RenderFrameImplMojoJsTest, EnableMojoJSBindingsWithBroker) {}

#if BUILDFLAG(PROTECTED_MEMORY_ENABLED)
using RenderFrameImplMojoJsDeathTest = RenderFrameImplMojoJsTest;
// Verifies that tampering with enabled_bindings_ to enable MojoJS bindings
// crashes.
TEST_F(RenderFrameImplMojoJsDeathTest, EnabledBindingsTampered) {
  GTEST_FLAG_SET(death_test_style, "threadsafe");

  // Should CHECK fail due to the bindings value differing from the protected
  // memory value.
  EXPECT_CHECK_DEATH_WITH(
      {
        GetMainRenderFrame()->enabled_bindings_.Put(
            BindingsPolicyValue::kMojoWebUi);

        LoadHTML(kSimpleScriptHtml);
      },
      "Check failed: \\*mojo_js_allowed_");
}

// Verifies that tampering with enable_mojo_js_bindings_ to enable MojoJS
// bindings crashes.
TEST_F(RenderFrameImplMojoJsDeathTest, EnableMojoJsBindingsTampered) {
  GTEST_FLAG_SET(death_test_style, "threadsafe");

  // Should CHECK fail due to the bindings value differing from the protected
  // memory value.
  EXPECT_CHECK_DEATH_WITH(
      {
        GetMainRenderFrame()->enable_mojo_js_bindings_ = true;

        LoadHTML(kSimpleScriptHtml);
      },
      "Check failed: \\*mojo_js_allowed_");
}

// Verifies that tampering with mojo_js_interface_broker_ to enable MojoJS
// bindings crashes.
TEST_F(RenderFrameImplMojoJsDeathTest, MojoJsInterfaceBrokerTampered) {
  GTEST_FLAG_SET(death_test_style, "threadsafe");

  // Should CHECK fail due to the bindings value differing from the protected
  // memory value.
  EXPECT_CHECK_DEATH_WITH(
      {
        GetMainRenderFrame()->mojo_js_interface_broker_ =
            TestRenderFrame::CreateStubBrowserInterfaceBrokerRemote();

        LoadHTML(kSimpleScriptHtml);
      },
      "Check failed: \\*mojo_js_allowed_");
}

// Verifies that tampering with mojo_js_interface_broker_ to enable MojoJS
// bindings crashes.
TEST_F(RenderFrameImplMojoJsDeathTest,
       ContextFeatureSettingsEnableMojoJsTampered) {
  GTEST_FLAG_SET(death_test_style, "threadsafe");

  // Should CHECK fail due to the bindings value differing from the protected
  // memory value.
  EXPECT_CHECK_DEATH_WITH(ContextFeatureSettingsEnableMojoJsTampered(),
                          "Check failed: \\*mojo_js_allowed_");
}
#endif  //  BUILDFLAG(PROTECTED_MEMORY_ENABLED)

}  // namespace content