// Copyright 2012 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include <list> #include <memory> #include <string> #include <utility> #include <vector> #include "base/functional/callback_helpers.h" #include "base/json/json_reader.h" #include "base/location.h" #include "base/memory/ptr_util.h" #include "base/memory/weak_ptr.h" #include "base/ranges/algorithm.h" #include "base/task/single_thread_task_runner.h" #include "base/test/bind.h" #include "base/test/run_until.h" #include "base/test/scoped_run_loop_timeout.h" #include "base/test/test_timeouts.h" #include "base/test/with_feature_override.h" #include "base/time/time.h" #include "build/build_config.h" #include "content/browser/fenced_frame/fenced_frame_url_mapping.h" #include "content/browser/renderer_host/cross_process_frame_connector.h" #include "content/browser/renderer_host/frame_tree.h" #include "content/browser/renderer_host/navigation_controller_impl.h" #include "content/browser/renderer_host/navigation_request.h" #include "content/browser/renderer_host/navigator.h" #include "content/browser/renderer_host/render_frame_host_impl.h" #include "content/browser/renderer_host/render_widget_host_view_child_frame.h" #include "content/browser/site_per_process_browsertest.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/common/content_navigation_policy.h" #include "content/public/browser/navigation_handle.h" #include "content/public/common/isolated_world_ids.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/content_browser_test_utils.h" #include "content/public/test/content_mock_cert_verifier.h" #include "content/shell/browser/shell.h" #include "content/test/content_browser_test_utils_internal.h" #include "content/test/render_document_feature.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 "testing/gtest/include/gtest/gtest.h" ElementsAre; WhenSorted; namespace content { namespace { void AddPagehideHandler(const ToRenderFrameHost& target, const char* message) { … } } // namespace // Tests that there are no crashes if a subframe is detached in its pagehide // handler. See https://crbug.com/590054. IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, DetachInPagehideHandler) { … } // Tests that trying to navigate in the pagehide handler doesn't crash the // browser. IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, NavigateInPagehideHandler) { … } // Verifies that when navigating an OOPIF to same site and then canceling // navigation from beforeunload handler popup will not remove the // RemoteFrameView from OOPIF's owner element in the parent process. This test // uses OOPIF visibility to make sure RemoteFrameView exists after beforeunload // is handled. IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, CanceledBeforeUnloadShouldNotClearRemoteFrameView) { … } // Ensure that after a main frame with an OOPIF is navigated cross-site, the // pagehide handler in the OOPIF sees correct main frame origin, namely the old // and not the new origin. See https://crbug.com/825283. IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, ParentOriginDoesNotChangeInPagehideHandler) { … } // Verify that when the last active frame in a process is going away as part of // OnUnload, the mojo::AgentSchedulingGroupHost::DidUnloadRenderFrame is // received prior to the process starting to shut down, ensuring that any // related unload work also happens before shutdown. See // https://crbug.com/867274 and https://crbug.com/794625. IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, UnloadACKArrivesPriorToProcessShutdownRequest) { … } // This is a regression test for https://crbug.com/891423 in which tabs showing // beforeunload dialogs stalled navigation and triggered the "hung process" // dialog. IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, NoCommitTimeoutWithBeforeUnloadDialog) { … } // Test that pagehide handlers in iframes are run, even when the removed subtree // is complicated with nested iframes in different processes. // A1 A1 // / \ / \ // B1 D --- Navigate ---> E D // / \ // C1 C2 // | | // B2 A2 // | // C3 IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, PagehideHandlerSubframes) { … } // Check that unload handlers in iframe don't prevents the main frame to be // deleted after a timeout. IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, SlowUnloadHandlerInIframe) { … } // Navigate from A(B(A(B)) to C. Check the pagehide handler are executed, // executed in the right order and the processes for A and B are removed. IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, PagehideHandlerABAB) { … } // Start with A(B(C)), navigate C to D and then B to E. By emulating a slow // unload handler in B,C and D, the end result is C is in pending deletion in B // and B is in pending deletion in A. // (1) (2) (3) //| | | | //| A | A | A | //| | | | | \ | //| B | B | B E | //| | | \ | \ | //| C | C D | C D | IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, UnloadNestedPendingDeletion) { … } // A set of nested frames A1(B1(A2)) are pending deletion because of a // navigation. This tests what happens if only A2 has a pagehide handler. // If B1's mojom::FrameHost::Detach is called before A2, it should not destroy // itself and its children, but rather wait for A2. IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, PartialPagehideHandler) { … } // Test RenderFrameHostImpl::PendingDeletionCheckCompletedOnSubtree. // // After a navigation commit, some children with no pagehide handler may be // eligible for immediate deletion. Several configurations are tested: // // Before navigation commit // // 0 | N : No pagehide handler // ‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑ | [N] : Pagehide handler // | | | | | | | | // [1] 2 [3] 5 7 9 12 | // | | | / \ / \ | // 4 [6] 8 10 11 13 [14] | // // After navigation commit (expected) // // 0 | N : No pagehide handler // --------------------- | [N] : Pagehide handler // | | | | | // [1] [3] 5 12 | // | \ | // [6] [14] | IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, PendingDeletionCheckCompletedOnSubtree) { … } // When an iframe is detached, check that pagehide handlers execute in all of // its child frames. Start from A(B(C)) and delete B from A. IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, DetachedIframePagehideHandlerABC) { … } #if defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER) || \ !defined(NDEBUG) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) // Too slow under sanitizers and debug builds, even with increased timeout: // https://crbug.com/1096612 // Disabled for Linux/Android due to failures: https://crbug.com/1494811 #define MAYBE_DetachedIframePagehideHandlerABCB … #else #define MAYBE_DetachedIframePagehideHandlerABCB … #endif // When an iframe is detached, check that pagehide handlers execute in all of // its child frames. Start from A(B1(C(B2))) and delete B1 from A. IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, MAYBE_DetachedIframePagehideHandlerABCB) { … } // When an iframe is detached, check that pagehide handlers execute in all of // its child frames. Start from A1(A2(B)), delete A2 from itself. IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, DetachedIframePagehideHandlerAAB) { … } // Tests that running layout from an pagehide handler inside teardown of the // RenderWidget (inside WidgetMsg_Close) can succeed. IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, RendererInitiatedWindowCloseWithPagehide) { … } // Regression test for https://crbug.com/960006. // // 1. Navigate to a1(a2(b3),c4), // 2. b3 has a slow unload handler. // 3. a2 navigates same process. // 4. When the new document is loaded, a message is sent to c4 to check it // cannot see b3 anymore, even if b3 is still unloading. IN_PROC_BROWSER_TEST_P( SitePerProcessBrowserTest, IsDetachedSubframeObservableDuringUnloadHandlerSameProcess) { … } // Regression test for https://crbug.com/960006. // // 1. Navigate to a1(a2(b3),c4), // 2. b3 has a slow unload handler. // 3. a2 navigates cross process. // 4. When the new document is loaded, a message is sent to c4 to check it // cannot see b3 anymore, even if b3 is still unloading. // // Note: This test is the same as the above, except it uses a cross-process // navigation at step 3. IN_PROC_BROWSER_TEST_P( SitePerProcessBrowserTest, IsDetachedSubframeObservableDuringUnloadHandlerCrossProcess) { … } // Regression test. https://crbug.com/963330 // 1. Start from A1(B2,C3) // 2. B2 is the "focused frame", is deleted and starts unloading. // 3. C3 commits a new navigation before B2 has completed its unload. IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, FocusedFrameUnload) { … } // Test the unload timeout is effective. IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, UnloadTimeout) { … } // Test that an unloading child can PostMessage its cross-process parent. IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, UnloadPostMessageToParentCrossProcess) { … } // Test that an unloading child can PostMessage its same-process parent. IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, UnloadPostMessageToParentSameProcess) { … } // Related to issue https://crbug.com/950625. // // 1. Start from A1(B1) // 2. Navigate A1 to A3, same-process. // 3. A1 requests the browser to detach B1, but this message is dropped. // 4. The browser must be resilient and detach B1 when A3 commits. // TODO(crbug.com/40914915): Fix flakes and re-enable test. IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, DISABLED_SameProcessNavigationResilientToDetachDropped) { … } #if BUILDFLAG(IS_LINUX) && defined(THREAD_SANITIZER) // See crbug.com/1275848. #define MAYBE_NestedSubframeWithPagehideHandler … #else #define MAYBE_NestedSubframeWithPagehideHandler … #endif // After a same-origin iframe navigation, check that grandchild iframe are // properly deleted and their pagehide handler executed. IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, MAYBE_NestedSubframeWithPagehideHandler) { … } // Some tests need an https server because third-party cookies are used, and // SameSite=None cookies must be Secure. This is a separate fixture due to // use the ContentMockCertVerifier. class SitePerProcessSSLBrowserTest : public SitePerProcessBrowserTest { … }; // Pagehide handlers should be able to do things that might require for instance // the RenderFrameHostImpl to stay alive. // - use console.log (handled via RFHI::DidAddMessageToConsole). // - use history.replaceState (handled via RFHI::OnUpdateState). // - use document.cookie // - use localStorage // // Test case: // 1. Start on A1(B2). B2 has a pagehide handler. // 2. Go to A3. // 3. Go back to A4(B5). // // TODO(crbug.com/41457585): history.replaceState is broken in OOPIFs. // // This test is similar to PagehideHandlersArePowerfulGrandChild, but with a // different frame hierarchy. // // TODO(crbug.com/40283595): investigate test flakes and re-enable test. IN_PROC_BROWSER_TEST_P(SitePerProcessSSLBrowserTest, DISABLED_PagehideHandlersArePowerful) { … } // Pagehide handlers should be able to do things that might require for instance // the RenderFrameHostImpl to stay alive. // - use console.log (handled via RFHI::DidAddMessageToConsole). // - use history.replaceState (handled via RFHI::OnUpdateState). // - use document.cookie // - use localStorage // // Test case: // 1. Start on A1(B2(C3)). C3 has an unload handler. // 2. Go to A4. // 3. Go back to A5(B6(C7)). // // TODO(crbug.com/41457585): history.replaceState is broken in OOPIFs. // // This test is similar to PagehideHandlersArePowerful, but with a different // frame hierarchy. // // TODO(crbug.com/40283595): investigate test flakes and re-enable test. IN_PROC_BROWSER_TEST_P(SitePerProcessSSLBrowserTest, DISABLED_PagehideHandlersArePowerfulGrandChild) { … } // Execute a pagehide handler from the initial empty document. // // Start from A1(B2(B3)). // B3 is the initial empty document created by B2. A pagehide handler is added // to B3. A1 deletes B2. IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, UnloadInInitialEmptyDocument) { … } INSTANTIATE_TEST_SUITE_P(…); // This test sets up a main frame which has an OOPIF. The main frame commits a // same-site navigation. The test then stops at the stage where the unload // handler of the OOPIF is running and the main frame RenderFrameHost's // `DocumentAssociatedData` is retrieved from the OOPIF. The test shows that // the `DocumentAssociatedData` is different from the one before navigation if // RenderDocument feature is not enabled for all frames. One place we have seen // this issue is in Protected Audience auctions. Please see crbug.com/1422301. IN_PROC_BROWSER_TEST_P( SitePerProcessBrowserTest, MainFrameDocumentAssociatedDataChangesOnSameSiteNavigation) { … } } // namespace content