chromium/content/browser/renderer_host/render_frame_host_manager_unittest.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/browser/renderer_host/render_frame_host_manager.h"

#include <stdint.h>

#include <set>
#include <string>
#include <tuple>
#include <unordered_set>
#include <utility>
#include <vector>

#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/hash/hash.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/renderer_host/navigation_controller_impl.h"
#include "content/browser/renderer_host/navigation_entry_impl.h"
#include "content/browser/renderer_host/navigation_request.h"
#include "content/browser/renderer_host/navigator.h"
#include "content/browser/renderer_host/render_frame_proxy_host.h"
#include "content/browser/site_info.h"
#include "content/browser/site_instance_group.h"
#include "content/browser/site_instance_impl.h"
#include "content/browser/webui/web_ui_controller_factory_registry.h"
#include "content/common/content_navigation_policy.h"
#include "content/common/features.h"
#include "content/public/browser/devtools_agent_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_widget_host.h"
#include "content/public/browser/render_widget_host_iterator.h"
#include "content/public/browser/render_widget_host_observer.h"
#include "content/public/browser/site_isolation_policy.h"
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_ui_controller.h"
#include "content/public/common/bindings_policy.h"
#include "content/public/common/content_features.h"
#include "content/public/common/javascript_dialog_type.h"
#include "content/public/common/url_constants.h"
#include "content/public/common/url_utils.h"
#include "content/public/test/back_forward_cache_util.h"
#include "content/public/test/fake_local_frame.h"
#include "content/public/test/fake_remote_frame.h"
#include "content/public/test/mock_render_process_host.h"
#include "content/public/test/scoped_web_ui_controller_factory_registration.h"
#include "content/public/test/test_utils.h"
#include "content/test/mock_widget_input_handler.h"
#include "content/test/navigation_simulator_impl.h"
#include "content/test/render_document_feature.h"
#include "content/test/test_content_browser_client.h"
#include "content/test/test_content_client.h"
#include "content/test/test_render_frame_host.h"
#include "content/test/test_render_view_host.h"
#include "content/test/test_render_widget_host.h"
#include "content/test/test_web_contents.h"
#include "net/base/load_flags.h"
#include "net/http/http_response_headers.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/frame/frame_policy.h"
#include "third_party/blink/public/common/tokens/tokens.h"
#include "third_party/blink/public/mojom/favicon/favicon_url.mojom.h"
#include "third_party/blink/public/mojom/frame/frame_owner_properties.mojom.h"
#include "third_party/blink/public/mojom/security_context/insecure_request_policy.mojom.h"
#include "ui/base/page_transition_types.h"
#include "url/origin.h"

#if BUILDFLAG(IS_ANDROID)
#include "content/public/browser/android/compositor.h"
#endif

namespace content {
namespace {

// VerifyPageFocusMessage from the mojo input handler.
void VerifyPageFocusMessage(TestRenderWidgetHost* twh, bool expected_focus) {}

class RenderFrameHostManagerTestWebUIControllerFactory
    : public WebUIControllerFactory {};

class BeforeUnloadFiredWebContentsDelegate : public WebContentsDelegate {};

class CloseWebContentsDelegate : public WebContentsDelegate {};

// This observer keeps track of the last deleted RenderViewHost to avoid
// accessing it and causing use-after-free condition.
class RenderViewHostDeletedObserver : public WebContentsObserver {};

// This observer keeps track of the last created RenderFrameHost to allow tests
// to ensure that no RenderFrameHost objects are created when not expected.
class RenderFrameHostCreatedObserver : public WebContentsObserver {};

// This observer is used to check whether IPC messages are being filtered for
// swapped out RenderFrameHost objects. It observes the plugin crash and favicon
// update events, which the FilterMessagesWhileSwappedOut test simulates being
// sent. The test is successful if the event is not observed.
// See http://crbug.com/351815
class PluginFaviconMessageObserver : public WebContentsObserver {};

// A shorter version for RenderFrameHostManager::DidNavigateFrame(rfh, ...).
// This provides all the arguments that aren't tested in this file.
void DidNavigateFrame(RenderFrameHostManager* rfh_manager,
                      RenderFrameHostImpl* rfh) {}

class TestDevToolsClientHost : public DevToolsAgentHostClient {};

}  // namespace

// Test that the "level" feature param has the expected effect.
class RenderDocumentFeatureTest : public testing::Test {};

TEST_F(RenderDocumentFeatureTest, FeatureDisabled) {}

TEST_F(RenderDocumentFeatureTest, LevelCrashed) {}

TEST_F(RenderDocumentFeatureTest, LevelNonLocalRootSubframe) {}

TEST_F(RenderDocumentFeatureTest, LevelSubframe) {}

TEST_F(RenderDocumentFeatureTest, LevelAllFrames) {}

class RenderFrameHostManagerTest
    : public RenderViewHostImplTestHarness,
      public ::testing::WithParamInterface<std::string> {};

// Tests that when you navigate from a chrome:// url to another page, and
// then do that same thing in another tab, that the two resulting pages have
// different SiteInstances, BrowsingInstances, and RenderProcessHosts. This is
// a regression test for bug 9364.
TEST_P(RenderFrameHostManagerTest, ChromeSchemeProcesses) {}

// Ensure that the browser ignores most IPC messages that arrive from a
// RenderViewHost that has been swapped out.  We do not want to take
// action on requests from a non-active renderer.  The main exception is
// for synchronous messages, which cannot be ignored without leaving the
// renderer in a stuck state.  See http://crbug.com/93427.
TEST_P(RenderFrameHostManagerTest, FilterMessagesWhileSwappedOut) {}

// Test that the UpdateFaviconURL function is ignored if the
// renderer is in the pending deletion state. The favicon code assumes
// that it only gets UpdateFaviconURL function for the most
// recently committed navigation for each WebContentsImpl.
TEST_P(RenderFrameHostManagerTest, UpdateFaviconURLWhilePendingUnload) {}

// Test if RenderViewHost::GetRenderWidgetHosts() only returns active
// widgets.
TEST_P(RenderFrameHostManagerTest, GetRenderWidgetHostsReturnsActiveViews) {}

// Test if RenderViewHost::GetRenderWidgetHosts() returns a subset of
// RenderViewHostImpl::GetAllRenderWidgetHosts().
// RenderViewHost::GetRenderWidgetHosts() returns only active widgets, but
// RenderViewHostImpl::GetAllRenderWidgetHosts() returns everything
// including inactive ones.
TEST_P(RenderFrameHostManagerTest,
       GetRenderWidgetHostsWithinGetAllRenderWidgetHosts) {}

// Test if SiteInstanceImpl::active_frame_count() is correctly updated
// as frames in a SiteInstance get replaced.
TEST_P(RenderFrameHostManagerTest, ActiveFrameCountWhileSwappingInAndOut) {}

// This deletes a WebContents when the given RVH is deleted. This is
// only for testing whether deleting an RVH does not cause any UaF in
// other parts of the system. For now, this class is only used for the
// next test cases to detect the bug mentioned at
// http://crbug.com/259859.
class RenderViewHostDestroyer : public WebContentsObserver {};

// Test if ShutdownRenderViewHostsInSiteInstance() does not touch any
// RenderWidget that has been freed while deleting a RenderViewHost in
// a previous iteration. This is a regression test for
// http://crbug.com/259859.
TEST_P(RenderFrameHostManagerTest,
       DetectUseAfterFreeInShutdownRenderViewHostsInSiteInstance) {}

// Stub out local frame mojo binding. Intercepts calls to EnableViewSourceMode
// and marks the message as received. This class attaches to the first
// RenderFrameHostImpl created.
class EnableViewSourceLocalFrame : public content::FakeLocalFrame,
                                   public WebContentsObserver {};

// When there is an error with the specified page, renderer exits view-source
// mode. See WebFrameImpl::DidFail(). We check by this test that
// EnableViewSourceMode message is sent on every navigation regardless
// `blink::WebView` is being newly created or reused.
TEST_P(RenderFrameHostManagerTest, AlwaysSendEnableViewSourceMode) {}

// Tests the Init function by checking the initial RenderViewHost.
TEST_P(RenderFrameHostManagerTest, Init) {}

// Tests the Navigate function. We navigate three sites consecutively and check
// how the pending/committed RenderViewHost are modified.
TEST_P(RenderFrameHostManagerTest, Navigate) {}

// Tests WebUI creation.
TEST_P(RenderFrameHostManagerTest, WebUI) {}

// Tests that we can open a WebUI link in a new tab from a WebUI page and still
// grant the correct bindings.  http://crbug.com/189101.
TEST_P(RenderFrameHostManagerTest, WebUIInNewTab) {}

// Tests that a WebUI is correctly reused between chrome:// pages.
TEST_P(RenderFrameHostManagerTest, WebUIWasReused) {}

// Tests that a WebUI is correctly cleaned up when navigating from a chrome://
// page to a non-chrome:// page.
TEST_P(RenderFrameHostManagerTest, WebUIWasCleared) {}

// Ensure that we can go back and forward even if a unload ACK isn't received.
// See http://crbug.com/93427.
TEST_P(RenderFrameHostManagerTest, NavigateAfterMissingUnloadACK) {}

// Test that we create `blink::RemoteFrame` objects for the opener chain when
// navigating an opened tab cross-process.  This allows us to support certain
// cross-process JavaScript calls (http://crbug.com/99202).
TEST_P(RenderFrameHostManagerTest, CreateProxiesForOpeners) {}

// Test that a page can disown the opener of the WebContents.
TEST_P(RenderFrameHostManagerTest, DisownOpener) {}

// Test that a page can disown a same-site opener of the WebContents.
TEST_P(RenderFrameHostManagerTest, DisownSameSiteOpener) {}

// Test that a page can disown the opener just as a cross-process navigation is
// in progress.
TEST_P(RenderFrameHostManagerTest, DisownOpenerDuringNavigation) {}

// Test that a page can disown the opener just after a cross-process navigation
// commits.
TEST_P(RenderFrameHostManagerTest, DisownOpenerAfterNavigation) {}

// Test that we clean up RenderFrameProxyHosts when a process hosting the
// associated frames crashes. http://crbug.com/258993
TEST_P(RenderFrameHostManagerTest, CleanUpProxiesOnProcessCrash) {}

// Test guest navigation behavior when navigating across sites.  Since guests
// support site isolation, we should swap guest SiteInstances as usual.
TEST_P(RenderFrameHostManagerTest, GuestNavigations) {}

namespace {

class WidgetDestructionObserver : public RenderWidgetHostObserver {};

}  // namespace

// Test that we cancel a pending RVH if we close the tab while it's pending.
// http://crbug.com/294697.
TEST_P(RenderFrameHostManagerTest, NavigateWithEarlyClose) {}

TEST_P(RenderFrameHostManagerTest, CloseWithPendingWhileUnresponsive) {}

TEST_P(RenderFrameHostManagerTest,
       CloseWithPendingWhileUnresponsiveWithDevTools) {}

// Tests that the RenderFrameHost is properly deleted when the
// mojo::AgentSchedulingGroupHost::DidUnloadRenderFrame is received.
// (mojo::FrameNavigationControl::Unload and the corresponding
// mojo::AgentSchedulingGroupHost::DidUnloadRenderFrame always occur after
// commit.) Also tests that an early
// mojo::AgentSchedulingGroupHost::DidUnloadRenderFrame is properly ignored.
TEST_P(RenderFrameHostManagerTest, DeleteFrameAfterUnloadACK) {}

// Tests that the RenderFrameHost is properly unloaded when the
// mojo::AgentSchedulingGroupHost::DidUnloadRenderFrame is received.
// (mojo::FrameNavigationControl::Unload and the corresponding
// mojo::AgentSchedulingGroupHost::DidUnloadRenderFrame always occur after
// commit.)
TEST_P(RenderFrameHostManagerTest, UnloadFrameAfterUnloadACK) {}

// Test that a RenderFrameHost is properly deleted if a navigation in the new
// renderer commits before sending the mojo::FrameNavigationControl::Unload
// message to the old renderer. This simulates a cross-site navigation to a
// synchronously committing URL (e.g., a data URL) and ensures it works
// properly.
TEST_P(RenderFrameHostManagerTest, CommitNewNavigationBeforeSendingUnload) {}

// Test that a RenderFrameHost is properly deleted when a cross-site navigation
// is cancelled.
TEST_P(RenderFrameHostManagerTest, CancelPendingProperlyDeletesOrSwaps) {}

class RenderFrameHostManagerTestWithSiteIsolation
    : public RenderFrameHostManagerTest {};

// Test that a pending RenderFrameHost in a non-root frame tree node is properly
// deleted when the node is detached. Motivated by http://crbug.com/441357 and
// http://crbug.com/444955.
TEST_P(RenderFrameHostManagerTestWithSiteIsolation, DetachPendingChild) {}

#if BUILDFLAG(IS_ANDROID)
// TODO(lukasza): https://crbug.com/1067432: Calling Compositor::Initialize()
// DCHECKs flakily and without such call the test below consistently fails on
// Android (DCHECKing about parent_view->GetFrameSinkId().is_valid() in
// RenderWidgetHostViewChildFrame::SetFrameConnectorDelegate).
#define MAYBE_TwoTabsCrashOneReloadsOneLeaves
#else
#define MAYBE_TwoTabsCrashOneReloadsOneLeaves
#endif
// Two tabs in the same process crash. The first tab is reloaded, and the second
// tab navigates away without reloading. The second tab's navigation shouldn't
// mess with the first tab's content. Motivated by http://crbug.com/473714.
TEST_P(RenderFrameHostManagerTestWithSiteIsolation,
       MAYBE_TwoTabsCrashOneReloadsOneLeaves) {}

// Tests two WebContents from the same origin, where one is first navigated to
// a different origin. This different origin experiences a Renderer crash.
// However we then navigate that WebContents back to the old origin, which still
// has an active Renderer.
//
// This test confirms that for this return navigation that we identified that
// there is no FallbackSurface for the RenderWidgetHostView to display during
// the navigation. (https://crbug.com/1258363)
TEST_P(RenderFrameHostManagerTestWithSiteIsolation,
       TwoTabsOneNavigatesAndCrashesThenNavigatesBack) {}

// Ensure that we don't grant WebUI bindings to a pending RenderViewHost when
// creating proxies for a non-WebUI subframe navigation.  This was possible due
// to the InitRenderView call from CreateRenderFrameProxy.
// See https://crbug.com/536145.
TEST_P(RenderFrameHostManagerTestWithSiteIsolation,
       DontGrantPendingWebUIToSubframe) {}

// This class intercepts RenderFrameProxyHost creations, and overrides their
// respective blink::mojom::RemoteFrame instances, so that it can watch the
// updates of opener frames.
class UpdateOpenerProxyObserver : public RenderFrameProxyHost::TestObserver {};

// Test that opener proxies are created properly with a cycle on the opener
// chain.
TEST_P(RenderFrameHostManagerTest, CreateOpenerProxiesWithCycleOnOpenerChain) {}

// Test that opener proxies are created properly when the opener points
// to itself.
TEST_P(RenderFrameHostManagerTest, CreateOpenerProxiesWhenOpenerPointsToSelf) {}

// Build the following frame opener graph and see that it can be properly
// traversed when creating opener proxies:
//
//     +-> root4 <--+   root3 <---- root2    +--- root1
//     |     /      |     ^         /  \     |    /  \     .
//     |    42      +-----|------- 22  23 <--+   12  13
//     |     +------------+            |             | ^
//     +-------------------------------+             +-+
//
// The test starts traversing openers from root1 and expects to discover all
// four FrameTrees.  Nodes 13 (with cycle to itself) and 42 (with back link to
// root3) should be put on the list of nodes that will need their frame openers
// set separately in a second pass, since their opener routing IDs won't be
// available during the first pass of CreateOpenerProxies.
TEST_P(RenderFrameHostManagerTest, TraverseComplexOpenerChain) {}

// This class intercepts RenderFrameProxyHost creations, and overrides their
// respective blink::mojom::RemoteFrame instances, so that it can watch the
// start and stop loading states.
class PageFocusProxyObserver : public RenderFrameProxyHost::TestObserver {};

// Check that when a window is focused/blurred, the message that sets
// page-level focus updates is sent to each process involved in rendering the
// current page.
//
// TODO(alexmos): Move this test to FrameTree unit tests once NavigateToEntry
// is moved to a common place.  See https://crbug.com/547275.
TEST_P(RenderFrameHostManagerTest, PageFocusPropagatesToSubframeProcesses) {}

// Check that page-level focus state is preserved across subframe navigations.
//
// TODO(alexmos): Move this test to FrameTree unit tests once NavigateToEntry
// is moved to a common place.  See https://crbug.com/547275.
TEST_P(RenderFrameHostManagerTest,
       PageFocusIsPreservedAcrossSubframeNavigations) {}

// Checks that a restore navigation to a WebUI works.
TEST_P(RenderFrameHostManagerTest, RestoreNavigationToWebUI) {}

// Simulates two simultaneous navigations involving one WebUI where the current
// RenderFrameHost commits.
TEST_P(RenderFrameHostManagerTest, SimultaneousNavigationWithOneWebUI1) {}

// Simulates two simultaneous navigations involving one WebUI where the new,
// cross-site RenderFrameHost commits.
TEST_P(RenderFrameHostManagerTest, SimultaneousNavigationWithOneWebUI2) {}

// Shared code until before commit for the SimultaneousNavigationWithTwoWebUIs*
// tests, accepting a lambda to execute the commit step.
// Simulates two simultaneous navigations involving two WebUIs where the current
// RenderFrameHost commits.
TEST_P(RenderFrameHostManagerTest, SimultaneousNavigationWithTwoWebUIs1) {}

// Simulates two simultaneous navigations involving two WebUIs where the new,
// cross-site RenderFrameHost commits.
TEST_P(RenderFrameHostManagerTest, SimultaneousNavigationWithTwoWebUIs2) {}

TEST_P(RenderFrameHostManagerTest, CanCommitOrigin) {}

// Tests that the correct intermediary and final navigation states are reached
// when navigating from a renderer that is not live to a WebUI URL.
TEST_P(RenderFrameHostManagerTest, NavigateFromDeadRendererToWebUI) {}

// Tests that the correct intermediary and final navigation states are reached
// when navigating same-site between two WebUIs of the same type.
TEST_P(RenderFrameHostManagerTest, NavigateSameSiteBetweenWebUIs) {}

// Tests that the correct intermediary and final navigation states are reached
// when navigating cross-site between two different WebUI types.
TEST_P(RenderFrameHostManagerTest, NavigateCrossSiteBetweenWebUIs) {}

// This class intercepts RenderFrameProxyHost creations, and overrides their
// respective blink::mojom::RemoteFrame instances.
class InsecureRequestPolicyProxyObserver
    : public RenderFrameProxyHost::TestObserver {};

// Tests that frame proxies receive updates when a frame's enforcement
// of insecure request policy changes.
TEST_P(RenderFrameHostManagerTestWithSiteIsolation,
       ProxiesReceiveInsecureRequestPolicy) {}

// This class intercepts RenderFrameProxyHost creations, and overrides their
// respective blink::mojom::RemoteFrame instances, so that it can watch the
// start and stop loading states.
class StartStopLoadingProxyObserver
    : public RenderFrameProxyHost::TestObserver {};

// Tests that new frame proxies receive an IPC to update their loading state,
// if they are created for a frame that's currently loading.  See
// https://crbug.com/916137.
TEST_P(RenderFrameHostManagerTestWithSiteIsolation,
       NewProxyReceivesLoadingState) {}

// Tests that a BeginNavigation IPC from a no longer active RFH in pending
// deletion state is ignored.
TEST_P(RenderFrameHostManagerTest,
       BeginNavigationIgnoredWhenInPendingDeletion) {}

// Run tests with BackForwardCache.
class RenderFrameHostManagerTestWithBackForwardCache
    : public RenderFrameHostManagerTest,
      public WebContentsDelegate {};

// Tests that a BeginNavigation IPC from a no longer active RFH in
// BackForwardCache is ignored. This test is a copy of
// "RenderFrameHostManagerTest.BeginNavigationIgnoredWhenInPendingDeletion" with
// BackForwardCache consideration.
TEST_P(RenderFrameHostManagerTestWithBackForwardCache,
       BeginNavigationIgnoredWhenInBackForwardCache) {}

// Check that after a navigation, the final SiteInstance has the correct
// original URL that was used to determine its site URL.
TEST_P(RenderFrameHostManagerTest,
       SiteInstanceOriginalURLIsPreservedAfterNavigation) {}

class AdTaggingSimulator : public WebContentsObserver {};

class AdStatusInterceptingRemoteFrame : public content::FakeRemoteFrame {};

class RenderFrameHostManagerAdTaggingSignalTest
    : public RenderFrameHostManagerTest,
      public RenderFrameProxyHost::TestObserver {};

// Test that when the proxy host is created for the local child frame to be
// swapped out (which occurs before UnfreezableFrameMsg_SwapOut IPC was sent),
// the frame replication state should already have the ad status set.
TEST_P(RenderFrameHostManagerAdTaggingSignalTest,
       AdStatusForLocalChildFrameToBeSwappedOut) {}

// A page with top frame A that has subframes B and A1. A1 is an ad iframe that
// does not commit. We expect that the proxy of A1 in B's process will receive
// an ad tagging signal.
TEST_P(RenderFrameHostManagerAdTaggingSignalTest,
       AdTagSignalForFrameProxyOfFrameThatDoesNotCommit) {}

// A page with top frame A that has subframes B and C. C is then navigated to an
// ad frame D. We expect that both the proxy of D in A's process and the proxy
// of D in B's process will receive an ad tagging signal.
TEST_P(RenderFrameHostManagerAdTaggingSignalTest,
       AdTagSignalForFrameProxyOfNewFrame) {}

// A page with top frame A that has an ad subframe B. Frame C is then created in
// A. We expect that when the proxy host of B in C's process is created, the
// frame's replication status already has the ad bit set, which will be
// propagated to the renderer side later.
TEST_P(RenderFrameHostManagerAdTaggingSignalTest,
       AdStatusForFrameProxyOfExistingFrameToNewFrame) {}

// Test a A(B(C)) setup where B and C are ads. The creation of C will trigger
// an ad tagging signal for the proxy of C in the process of A.
TEST_P(RenderFrameHostManagerAdTaggingSignalTest, RemoteGrandchildAdTagSignal) {}

INSTANTIATE_TEST_SUITE_P();
INSTANTIATE_TEST_SUITE_P();
INSTANTIATE_TEST_SUITE_P();
INSTANTIATE_TEST_SUITE_P();
}  // namespace content