// 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