chromium/content/browser/renderer_host/proactively_swap_browsing_instances_browsertest.cc

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <string_view>

#include "base/test/scoped_feature_list.h"
#include "content/browser/renderer_host/navigation_controller_impl.h"
#include "content/browser/renderer_host/navigation_request.h"
#include "content/browser/renderer_host/render_frame_host_manager_browsertest.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/common/content_navigation_policy.h"
#include "content/public/common/content_switches.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.h"
#include "content/public/test/content_browser_test_content_browser_client.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/content_mock_cert_verifier.h"
#include "content/public/test/fenced_frame_test_util.h"
#include "content/public/test/test_frame_navigation_observer.h"
#include "content/public/test/test_navigation_observer.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/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/default_handlers.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "services/network/public/cpp/features.h"

namespace content {

namespace {

// Helper function for error page navigations that makes sure that the last
// committed origin on |node| is an opaque origin with a precursor that matches
// |url|'s origin.
// Returns true if the frame has an opaque origin with the expected precursor
// information. Otherwise returns false.
bool IsOriginOpaqueAndCompatibleWithURL(FrameTreeNode* node, const GURL& url) {}

bool IsMainFrameOriginOpaqueAndCompatibleWithURL(Shell* shell,
                                                 const GURL& url) {}

bool HasErrorPageSiteInfo(SiteInstance* site_instance) {}

class BrowsingContextGroupSwapObserver : public WebContentsObserver {};

}  // namespace

class ProactivelySwapBrowsingInstancesTest : public RenderFrameHostManagerTest {};

// Test to ensure that the error page navigation does not change
// BrowsingInstances when window.open is present.
IN_PROC_BROWSER_TEST_P(
    ProactivelySwapBrowsingInstancesTest,
    ErrorPageNavigationWithWindowOpenDoesNotChangeBrowsingInstance) {}

IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesTest,
                       ReloadShouldNotChangeBrowsingInstance) {}

class ProactivelySwapBrowsingInstancesCrossSiteDoesNotReuseProcessTest
    : public RenderFrameHostManagerTest {};

// When we do a BrowsingInstance swap on renderer-initiated cross-site
// navigation, the current process will not be reused.
IN_PROC_BROWSER_TEST_P(
    ProactivelySwapBrowsingInstancesCrossSiteDoesNotReuseProcessTest,
    RendererInitiatedCrossSiteNavigationDoesNotReuseProcess) {}

// Different from renderer-initiated cross-site navigations, browser-initiated
// cross-site navigations do swap BrowsingInstances and processes without
// ProactivelySwapBrowsingInstance. Because of that, we shouldn't reuse the
// process for the new BrowsingInstance.
IN_PROC_BROWSER_TEST_P(
    ProactivelySwapBrowsingInstancesCrossSiteDoesNotReuseProcessTest,
    BrowserInitiatedCrossSiteNavigationDoesNotReuseProcess) {}

// A test ContentBrowserClient implementation that enforce process-per-site mode
// if |should_use_process_per_site_| is true. It is used to verify that we don't
// reuse the current page's renderer process when navigating to sites that uses
// process-per-site.
class ProcessPerSiteContentBrowserClient
    : public ContentBrowserTestContentBrowserClient {};

// We should not reuse the current process on renderer-initiated navigations to
// sites that needs to use process-per-site, and should create a new process for
// the site if there isn't already a process for that site.
IN_PROC_BROWSER_TEST_P(
    ProactivelySwapBrowsingInstancesCrossSiteDoesNotReuseProcessTest,
    RendererInitiatedCrossSiteNavigationToProcessPerSiteURLCreatesNewProcess) {}

// We should not reuse the current process on renderer-initiated navigations to
// sites that needs to use process-per-site, and should use the sole process for
// that site if it already exists.
IN_PROC_BROWSER_TEST_P(
    ProactivelySwapBrowsingInstancesCrossSiteDoesNotReuseProcessTest,
    RendererInitiatedCrossSiteNavigationToProcessPerSiteURLUsesProcessForSite) {}

// We should not reuse the current process on renderer-initiated navigations to
// sites that require a dedicated process.
IN_PROC_BROWSER_TEST_P(
    ProactivelySwapBrowsingInstancesCrossSiteDoesNotReuseProcessTest,
    NavigationToSiteThatRequiresDedicatedProcess) {}

// We should not reuse the current process on renderer-initiated navigations to
// sites that require a dedicated process.
IN_PROC_BROWSER_TEST_P(
    ProactivelySwapBrowsingInstancesCrossSiteDoesNotReuseProcessTest,
    NavigationFromSiteThatRequiresDedicatedProcess) {}

IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesTest,
                       RendererInitiatedSameSiteNavigationReusesProcess) {}

IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesTest,
                       BrowserInitiatedSameSiteNavigationReusesProcess) {}

// Tests that navigations that started but haven't committed yet will be
// overridden by navigations started later if both navigations created
// speculative RFHs.
IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesTest,
                       MultipleNavigationsStarted) {}

// Tests history same-site process reuse:
// 1. Visit A1, A2, B.
// 2. Go back to A2 (should use new process).
// 3. Go back to A1 (should reuse A2's process).
IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesTest,
                       HistoryNavigationReusesProcess) {}

// Tests history same-site process reuse:
// 1. Visit A1, A2, B.
// 2. Go back two entries to A1 (should use new process).
// 3. Go forward to A2 (should reuse A1's process).
IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesTest,
                       HistoryNavigationReusesProcess_SkipSameSiteEntry) {}

// Tests history same-site process reuse:
// 1. Visit A1, B, A3.
// 2. Go back two entries to A1 (should use A3's process).
// 3. Go forward to B (should use new process).
IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesTest,
                       HistoryNavigationReusesProcess_SkipCrossSiteEntry) {}

// Tests history same-site process reuse:
// 1. Visit A1 (which window.opens A2) then B.
// 2. Visit A3, which should use a new process (can't use A2's process).
// 2. Go back two entries to A1 (should use A2's process - the same process it
// used originally).
IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesTest,
                       HistoryNavigationReusesProcessThatIsStillAlive) {}

// If the navigation is same-document or ends up using the same NavigationEntry
// (e.g., enter in omnibox converted to a reload), we should not do a proactive
// BrowsingInstance swap.
IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesTest,
                       SameEntryAndSameDocumentNavigationDoesNotSwap) {}

IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesTest,
                       ReloadDoesNotSwap) {}

// Regression test for crbug.com/340606786. This test ensures that the browser
// doesn't crash if a reload happens on a post-commit error page.
IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesTest,
                       ReloadPostCommitErrorPage) {}

class ProactivelySwapBrowsingInstancesTestWithoutSpeculativeRFHDelay
    : public ProactivelySwapBrowsingInstancesTest {};

// The test disables delaying the speculative RFH creation when navigation
// starts since it checks the behavior of speculative RFH during redirection.
IN_PROC_BROWSER_TEST_P(
    ProactivelySwapBrowsingInstancesTestWithoutSpeculativeRFHDelay,
    SwapOnNavigationToPageThatRedirects) {}

IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesTest,
                       DoNotSwapWhenReplacingHistoryEntry) {}

// When we do a same-document navigation from A to A#foo then a navigation that
// does replacement (e.g., cross-process reload, or location.replace, or other
// client redirects) such that B takes the place of A#foo, we can go back to A
// with the back navigation. In this case, we might want to do a proactive BI
// swap so that page A can be bfcached.
// However, this test is currently disabled because we won't swap on any
// navigation that will replace the current history entry.
// TODO(rakina): Support this case.
IN_PROC_BROWSER_TEST_P(
    ProactivelySwapBrowsingInstancesTest,
    DISABLED_ShouldSwapWhenReplacingEntryWithSameDocumentPreviousEntry) {}

IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesTest,
                       DoNotSwapWhenRelatedContentsPresent) {}

// We should reuse the current process on same-site navigations even if the
// site requires a dedicated process (because we are still in the same site).
IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesTest,
                       NavigationToSiteThatRequiresDedicatedProcess) {}

// Tests that pagehide handlers of the old RFH are run during the commit
// of the new RFH when swapping RFH for same-site navigations due to proactive
// BrowsingInstance swap.
IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesTest,
                       PagehideRunsDuringCommit) {}

// Tests that visibilitychange handlers of the old RFH are run during the commit
// of the new RFH when swapping RFH for same-site navigations due to proactive
// BrowsingInstance swap.
IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesTest,
                       VisibilitychangeRunsDuringCommit) {}

// Tests that unload handlers of the old RFH are run during commit of the new
// RFH when swapping RFH for same-site navigations due to proactive
// BrowsingInstance swap.
// TODO(crbug.com/40142288): support this.
IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesTest,
                       DISABLED_UnloadRunsDuringCommit) {}

// Tests that pagehide and visibilitychange handlers of a subframe in the old
// page are run during the commit of a new main RFH when swapping RFH for
// same-site navigations due to proactive BrowsingInstance swap.
IN_PROC_BROWSER_TEST_P(
    ProactivelySwapBrowsingInstancesTest,
    PagehideAndVisibilitychangeInSubframesAreRunDuringCommit) {}

// Tests that pagehide handlers of the old RFH are run during the commit
// of the new RFH when swapping RFH for same-site navigations due to proactive
// BrowsingInstance swap even if the page is already hidden (and
// visibilitychange won't run).
IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesTest,
                       PagehideRunsDuringCommitOfHiddenPage) {}

// To address breakage of named window reuse across a back navigation
// when a proactive BrowsingInstance swap has happened, we offer the
// option to use an explicit opener relation for same-window navigations
// as an opt-out from proactive swaps. These tests cover cases that should (and
// should not) have the opt-out mechanism take effect.
class ProactivelySwapBrowsingInstancesOptOutTest
    : public ProactivelySwapBrowsingInstancesTest {};

IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesOptOutTest,
                       DoNotSwapWithAnchorRelOpener) {}

IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesOptOutTest,
                       DoNotSwapWithWindowOpener) {}

IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesOptOutTest,
                       DoNotSwapWithFormRelOpener) {}

IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesOptOutTest,
                       PreventingSwapAllowsFutureScripting) {}

// Like PreventingSwapAllowsFutureScripting, but performs another navigation
// after the back navigation, to confirm that the next page can also reuse the
// opened window.
IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesOptOutTest,
                       PreventingSwapAllowsFutureScriptingAfterAdditionalNav) {}

IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesOptOutTest,
                       DoNotSwapWithAnchorRelOpenerCrossSite) {}

IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesOptOutTest,
                       DoNotSwapWithAnchorRelOpenerWithParent) {}

IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesOptOutTest,
                       DoNotSwapWithAnchorRelOpenerInOopif) {}

IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesOptOutTest,
                       DoNotSwapWithAnchorRelOpenerOpenURL) {}

IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesOptOutTest,
                       CannotOptOutOfCoop) {}

IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesOptOutTest,
                       CannotOptOutFromFencedFrame) {}

IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesOptOutTest,
                       RelOpenerAndNoopener) {}

class ProactivelySwapBrowsingInstancesSameSiteCoopTest
    : public ProactivelySwapBrowsingInstancesTest {};

// Tests history same-site process reuse:
// 1. Visit A1 (non-COOP), A2 (non-COOP, should reuse A1's process), A3 (uses
// COOP + COEP, should use new process).
// 2. Go back to A2 (should use new process).
// 3. Go back to A1 (should reuse A2's process).
IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesSameSiteCoopTest,
                       HistoryNavigationReusesProcess_COOP) {}

// Tests that enable clearing window.name on on cross-site
// cross-BrowsingInstance navigations when
// ProactivelySwapBrowsingInstancesSameSite is enabled.
class ProactivelySwapBrowsingInstancesSameSiteClearWindowNameTest
    : public ProactivelySwapBrowsingInstancesTest {};

// Verify that same-site main frame navigation that swaps BrowsingInstances
// does not clear window.name.
IN_PROC_BROWSER_TEST_P(
    ProactivelySwapBrowsingInstancesSameSiteClearWindowNameTest,
    NotClearWindowNameSameSite) {}

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