chromium/content/browser/site_per_process_browsertest.cc

// 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 "content/browser/site_per_process_browsertest.h"

#include <stddef.h>
#include <stdint.h>

#include <algorithm>
#include <cmath>
#include <list>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <tuple>
#include <utility>
#include <vector>

#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#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/raw_ptr.h"
#include "base/memory/raw_ref.h"
#include "base/memory/scoped_refptr.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/scoped_observation.h"
#include "base/strings/pattern.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/run_until.h"
#include "base/test/test_future.h"
#include "base/test/test_timeouts.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "cc/base/math_util.h"
#include "cc/input/touch_action.h"
#include "components/input/input_router.h"
#include "components/input/render_widget_host_input_event_router.h"
#include "components/input/switches.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/gpu/compositor_util.h"
#include "content/browser/process_lock.h"
#include "content/browser/renderer_host/agent_scheduling_group_host.h"
#include "content/browser/renderer_host/cross_process_frame_connector.h"
#include "content/browser/renderer_host/frame_navigation_entry.h"
#include "content/browser/renderer_host/frame_tree.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_entry_restore_context_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_frame_proxy_host.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_view_child_frame.h"
#include "content/browser/site_info.h"
#include "content/browser/storage_partition_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/common/content_navigation_policy.h"
#include "content/common/frame.mojom-test-utils.h"
#include "content/common/input/actions_parser.h"
#include "content/common/input/synthetic_gesture.h"
#include "content/common/input/synthetic_gesture_target.h"
#include "content/common/input/synthetic_pinch_gesture_params.h"
#include "content/common/input/synthetic_pointer_action.h"
#include "content/common/input/synthetic_tap_gesture.h"
#include "content/common/input/synthetic_touchscreen_pinch_gesture.h"
#include "content/common/renderer.mojom.h"
#include "content/common/renderer_host.mojom-test-utils.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/context_menu_params.h"
#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/javascript_dialog_manager.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/render_process_host_priority_client.h"
#include "content/public/browser/site_isolation_policy.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.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_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/hit_test_region_observer.h"
#include "content/public/test/navigation_handle_observer.h"
#include "content/public/test/policy_container_utils.h"
#include "content/public/test/render_frame_host_test_support.h"
#include "content/public/test/test_devtools_protocol_client.h"
#include "content/public/test/test_frame_navigation_observer.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/public/test/test_navigation_throttle.h"
#include "content/public/test/test_navigation_throttle_inserter.h"
#include "content/public/test/test_utils.h"
#include "content/public/test/url_loader_interceptor.h"
#include "content/shell/browser/shell.h"
#include "content/shell/common/main_frame_counter_test_impl.h"
#include "content/shell/common/shell_switches.h"
#include "content/test/content_browser_test_utils_internal.h"
#include "content/test/did_commit_navigation_interceptor.h"
#include "content/test/render_document_feature.h"
#include "ipc/constants.mojom.h"
#include "ipc/ipc_security_test_util.h"
#include "media/base/media_switches.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/test_support/test_utils.h"
#include "net/base/url_util.h"
#include "net/dns/mock_host_resolver.h"
#include "net/http/mock_http_cache.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 "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/cpp/web_sandbox_flags.h"
#include "services/network/public/mojom/web_sandbox_flags.mojom-shared.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/input/web_input_event.h"
#include "third_party/blink/public/common/permissions_policy/origin_with_possible_wildcards.h"
#include "third_party/blink/public/common/permissions_policy/permissions_policy.h"
#include "third_party/blink/public/common/permissions_policy/policy_value.h"
#include "third_party/blink/public/common/switches.h"
#include "third_party/blink/public/common/tokens/tokens.h"
#include "third_party/blink/public/mojom/frame/frame.mojom-test-utils.h"
#include "third_party/blink/public/mojom/frame/frame.mojom.h"
#include "third_party/blink/public/mojom/frame/frame_replication_state.mojom.h"
#include "third_party/blink/public/mojom/leak_detector/leak_detector.mojom-test-utils.h"
#include "third_party/blink/public/mojom/leak_detector/leak_detector.mojom.h"
#include "third_party/blink/public/mojom/page/widget.mojom-test-utils.h"
#include "third_party/blink/public/mojom/security_context/insecure_request_policy.mojom.h"
#include "ui/display/display_switches.h"
#include "ui/display/screen.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/blink/blink_features.h"
#include "ui/events/event.h"
#include "ui/events/event_utils.h"
#include "ui/events/keycodes/dom/dom_code.h"
#include "ui/events/keycodes/dom/dom_key.h"
#include "ui/events/keycodes/keyboard_codes.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/transform.h"
#include "ui/latency/latency_info.h"
#include "ui/native_theme/native_theme_features.h"

#if defined(USE_AURA)
#include "content/browser/renderer_host/render_widget_host_view_aura.h"
#include "ui/aura/window.h"
#endif

#if BUILDFLAG(IS_ANDROID)
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h"
#include "content/browser/android/gesture_listener_manager.h"
#include "content/browser/android/ime_adapter_android.h"
#include "content/browser/renderer_host/input/touch_selection_controller_client_manager_android.h"
#include "content/browser/renderer_host/render_widget_host_view_android.h"
#include "content/browser/web_contents/web_contents_view_android.h"
#include "content/public/browser/android/child_process_importance.h"
#include "content/test/mock_overscroll_refresh_handler_android.h"
#include "ui/android/view_android.h"
#include "ui/android/window_android.h"
#include "ui/events/android/event_handler_android.h"
#include "ui/events/android/motion_event_android.h"
#include "ui/gfx/geometry/point_f.h"
#endif

SizeIs;
WhenSorted;
ElementsAre;

namespace content {

namespace {

void VerifyChildProcessHasMainFrame(
    mojo::Remote<mojom::MainFrameCounterTest>& main_frame_counter,
    bool expected_state) {}

CrashVisibility;

// Helper function to send a postMessage and wait for a reply message.  The
// |post_message_script| is executed on the |sender_ftn| frame, and the sender
// frame is expected to post |reply_status| from the DOMAutomationController
// when it receives a reply.
void PostMessageAndWaitForReply(FrameTreeNode* sender_ftn,
                                const std::string& post_message_script,
                                const std::string& reply_status) {}

// Helper function to extract and return "window.receivedMessages" from the
// |sender_ftn| frame.  This variable is used in post_message.html to count the
// number of messages received via postMessage by the current window.
int GetReceivedMessages(FrameTreeNode* ftn) {}

// Helper function to perform a window.open from the |caller_frame| targeting a
// frame with the specified name.
void NavigateNamedFrame(const ToRenderFrameHost& caller_frame,
                        const GURL& url,
                        const std::string& name) {}

// Helper function to generate a click on the given RenderWidgetHost.  The
// mouse event is forwarded directly to the RenderWidgetHost without any
// hit-testing.
void SimulateMouseClick(RenderWidgetHost* rwh, int x, int y) {}

// Retrieve self.origin for the frame |ftn|.
EvalJsResult GetOriginFromRenderer(FrameTreeNode* ftn) {}

// This observer detects when WebContents receives notification of a user
// gesture having occurred, following a user input event targeted to
// a RenderWidgetHost under that WebContents.
class UserInteractionObserver : public WebContentsObserver {};

// Supports waiting until a WebContents notifies its observers that the visible
// security state changed, and a test-specific condition is true at that time.
class VisibleSecurityStateObserver : public WebContentsObserver {};

// Helper function to focus a frame by sending it a mouse click and then
// waiting for it to become focused.
void FocusFrame(FrameTreeNode* frame) {}

bool ConvertJSONToPoint(const std::string& str, gfx::PointF* point) {}

// Helper function to generate a permissions policy for a single feature and a
// list of origins. (Equivalent to the declared policy "feature origin1 origin2
// ...".) If the origins list is empty, it's treated as matches all origins
// (Equivalent to the declared policy "feature *")
blink::ParsedPermissionsPolicyDeclaration
CreateParsedPermissionsPolicyDeclaration(
    blink::mojom::PermissionsPolicyFeature feature,
    const std::vector<GURL>& origins,
    bool match_all_origins = false,
    const std::optional<GURL> self_if_matches = std::nullopt) {}

blink::ParsedPermissionsPolicy CreateParsedPermissionsPolicy(
    const std::vector<blink::mojom::PermissionsPolicyFeature>& features,
    const std::vector<GURL>& origins,
    bool match_all_origins = false,
    const std::optional<GURL> self_if_matches = std::nullopt) {}

blink::ParsedPermissionsPolicy CreateParsedPermissionsPolicyMatchesSelf(
    const std::vector<blink::mojom::PermissionsPolicyFeature>& features,
    const GURL& self_if_matches) {}

blink::ParsedPermissionsPolicy CreateParsedPermissionsPolicyMatchesAll(
    const std::vector<blink::mojom::PermissionsPolicyFeature>& features) {}

blink::ParsedPermissionsPolicy CreateParsedPermissionsPolicyMatchesNone(
    const std::vector<blink::mojom::PermissionsPolicyFeature>& features) {}

// Check frame depth on node, widget, and process all match expected depth.
void CheckFrameDepth(unsigned int expected_depth, FrameTreeNode* node) {}

void GenerateTapDownGesture(RenderWidgetHost* rwh) {}

}  // namespace

//
// SitePerProcessBrowserTestBase
//

SitePerProcessBrowserTestBase::SitePerProcessBrowserTestBase() {}

std::string SitePerProcessBrowserTestBase::DepictFrameTree(
    FrameTreeNode* node) {}

std::string SitePerProcessBrowserTestBase::WaitForMessageScript(
    const std::string& result_expression) {}

void SitePerProcessBrowserTestBase::SetUpCommandLine(
    base::CommandLine* command_line) {}

void SitePerProcessBrowserTestBase::SetUpOnMainThread() {}

void SitePerProcessBrowserTestBase::ForceUpdateViewportIntersection(
    FrameTreeNode* frame_tree_node,
    const blink::mojom::ViewportIntersectionState& intersection_state) {}

void SitePerProcessBrowserTestBase::RunPostedTasks() {}

// SitePerProcessBrowserTest

SitePerProcessBrowserTest::SitePerProcessBrowserTest() {}

std::string SitePerProcessBrowserTest::GetExpectedOrigin(
    const std::string& host) {}

// SitePerProcessIgnoreCertErrorsBrowserTest

void SitePerProcessIgnoreCertErrorsBrowserTest::SetUpOnMainThread() {}

void SitePerProcessIgnoreCertErrorsBrowserTest::SetUpCommandLine(
    base::CommandLine* command_line) {}

void SitePerProcessIgnoreCertErrorsBrowserTest::
    SetUpInProcessBrowserTestFixture() {}

void SitePerProcessIgnoreCertErrorsBrowserTest::
    TearDownInProcessBrowserTestFixture() {}

// SitePerProcessAutoplayBrowserTest

class SitePerProcessAutoplayBrowserTest : public SitePerProcessBrowserTest {};

// Certain tests require the speculative RFH to be created before the browser
// receives any data from the server. The delay of creating the RFH is set to 0
// in these tests so that the speculative RFH is created when the request is
// sent.
class SitePerProcessBrowserTestWithoutSpeculativeRFHDelay
    : public SitePerProcessBrowserTest {};

// Ensure that navigating subframes in --site-per-process mode works and the
// correct documents are committed.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, CrossSiteIframe) {}

// Ensure that processes for iframes correctly track whether or not they have a
// local main frame.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       CrossSiteIframeMainFrameCount) {}

// Ensure that title updates affect the correct NavigationEntry after a new
// subframe navigation with an out-of-process iframe.  https://crbug.com/616609.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, TitleAfterCrossSiteIframe) {}

// This test verifies that scroll bubbling from an OOPIF properly forwards
// GestureFlingStart events from the child frame to the parent frame. This
// test times out on failure.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       GestureFlingStartEventsBubble) {}

// Test that fling on an out-of-process iframe progresses properly.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       TouchscreenGestureFlingStart) {}

// Test that fling on an out-of-process iframe progresses properly.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, TouchpadGestureFlingStart) {}

// Tests OOPIF rendering by checking that the RWH of the iframe generates
// OnSwapCompositorFrame message.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, CompositorFrameSwapped) {}

// Ensure that OOPIFs are deleted after navigating to a new main frame.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, CleanupCrossSiteIframe) {}

IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, NavigateRemoteFrame) {}

IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       NavigateRemoteFrameToBlankAndDataURLs) {}

// This test checks that killing a renderer process of a remote frame
// and then navigating some other frame to the same SiteInstance of the killed
// process works properly.
// This can be illustrated as follows,
// where 1/2/3 are FrameTreeNode-s and A/B are processes and B* is the killed
// B process:
//
//     1        A                  A                           A
//    / \  ->  / \  -> Kill B ->  / \  -> Navigate 3 to B ->  / \  .
//   2   3    B   A              B*  A                       B*  B
//
// Initially, node1.proxy_hosts_ = {B}
// After we kill B, we make sure B stays in node1.proxy_hosts_, then we navigate
// 3 to B and we expect that to complete normally.
// See http://crbug.com/432107.
//
// Note that due to http://crbug.com/450681, node2 cannot be re-navigated to
// site B and stays in not rendered state.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       NavigateRemoteFrameToKilledProcess) {}

// This test ensures that WebContentsImpl::FocusOwningWebContents does not crash
// the browser if the currently focused frame's renderer has disappeared.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, RemoveFocusFromKilledFrame) {}

// This test is similar to
// SitePerProcessBrowserTest.NavigateRemoteFrameToKilledProcess with
// addition that node2 also has a cross-origin frame to site C.
//
//     1          A                  A                       A
//    / \        / \                / \                     / \  .
//   2   3 ->   B   A -> Kill B -> B*   A -> Navigate 3 -> B*  B
//  /          /
// 4          C
//
// Initially, node1.proxy_hosts_ = {B, C}
// After we kill B, we make sure B stays in node1.proxy_hosts_, but
// C gets cleared from node1.proxy_hosts_.
//
// Note that due to http://crbug.com/450681, node2 cannot be re-navigated to
// site B and stays in not rendered state.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       NavigateRemoteFrameToKilledProcessWithSubtree) {}

// Ensure that the renderer process doesn't crash when the main frame navigates
// a remote child to a page that results in a network error.
// See https://crbug.com/558016.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, NavigateRemoteAfterError) {}

// Ensure that a cross-site page ends up in the correct process when it
// successfully loads after earlier encountering a network error for it.
// See https://crbug.com/560511.
// TODO(creis): Make the net error page show in the correct process as well,
// per https://crbug.com/588314.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, ProcessTransferAfterError) {}

// Verify that killing a cross-site frame's process B and then navigating a
// frame to B correctly recreates all proxies in B.
//
//      1           A                    A          A
//    / | \       / | \                / | \      / | \  .
//   2  3  4 ->  B  A  A -> Kill B -> B* A  A -> B* B  A
//
// After the last step, the test sends a postMessage from node 3 to node 4,
// verifying that a proxy for node 4 has been recreated in process B.  This
// verifies the fix for https://crbug.com/478892.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       NavigatingToKilledProcessRestoresAllProxies) {}

// Verify that proxy creation doesn't recreate a crashed process if no frame
// will be created in it.
//
//      1           A                    A          A
//    / | \       / | \                / | \      / | \    .
//   2  3  4 ->  B  A  A -> Kill B -> B* A  A -> B* A  A
//                                                      \  .
//                                                       A
//
// The test kills process B (node 2), creates a child frame of node 4 in
// process A, and then checks that process B isn't resurrected to create a
// proxy for the new child frame.  See https://crbug.com/476846.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       CreateChildFrameAfterKillingProcess) {}

// Verify that creating a child frame after killing and reloading an opener
// process doesn't crash. See https://crbug.com/501152.
//   1. Navigate to site A.
//   2. Open a popup with window.open and navigate it cross-process to site B.
//   3. Kill process A for the original tab.
//   4. Reload the original tab to resurrect process A.
//   5. Add a child frame to the top-level frame in the popup tab B.
// In step 5, we try to create proxies for the child frame in all SiteInstances
// for which its parent has proxies.  This includes A.  However, even though
// process A is live (step 4), the parent proxy in A is not live (which was
// incorrectly assumed previously).  This is because step 4 does not resurrect
// proxies for popups opened before the crash.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       CreateChildFrameAfterKillingOpener) {}

// In A-embed-B-embed-C scenario, verify that killing process B clears proxies
// of C from the tree.
//
//     1          A                  A
//    / \        / \                / \    .
//   2   3 ->   B   A -> Kill B -> B*  A
//  /          /
// 4          C
//
// node1 is the root.
// Initially, both node1.proxy_hosts_ and node3.proxy_hosts_ contain C.
// After we kill B, make sure proxies for C are cleared.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       KillingRendererClearsDescendantProxies) {}

// Crash a subframe and ensures its children are cleared from the FrameTree.
// See http://crbug.com/338508.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, CrashSubframe) {}

// When a new subframe is added, related SiteInstances that can reach the
// subframe should create proxies for it (https://crbug.com/423587).  This test
// checks that if A embeds B and later adds a new subframe A2, A2 gets a proxy
// in B's process.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, CreateProxiesForNewFrames) {}

// TODO(nasko): Disable this test until out-of-process iframes is ready and the
// security checks are back in place.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       DISABLED_CrossSiteIframeRedirectOnce) {}

// TODO(nasko): Disable this test until out-of-process iframes is ready and the
// security checks are back in place.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       DISABLED_CrossSiteIframeRedirectTwice) {}

// Ensure that when navigating a frame cross-process RenderFrameProxyHosts are
// created in the FrameTree skipping the subtree of the navigating frame (but
// not the navigating frame itself).
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, ProxyCreationSkipsSubtree) {}

// Verify origin replication with an A-embed-B-embed-C-embed-A hierarchy.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, OriginReplication) {}

// Test that HasReceivedUserGesture and HasReceivedUserGestureBeforeNavigation
// are propagated correctly across origins.
// TODO(crbug.com/40653035): This test is flaky.
IN_PROC_BROWSER_TEST_P(SitePerProcessAutoplayBrowserTest,
                       DISABLED_PropagateUserGestureFlag) {}

// Check that iframe sandbox flags are replicated correctly.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, SandboxFlagsReplication) {}

// Check that dynamic updates to iframe sandbox flags are propagated correctly.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, DynamicSandboxFlags) {}

// Check that dynamic updates to iframe sandbox flags are propagated correctly.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       DynamicSandboxFlagsRemoteToLocal) {}

// Check that dynamic updates to iframe sandbox flags are propagated correctly.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       DynamicSandboxFlagsRendererInitiatedNavigation) {}

// Verify that when a new child frame is added, the proxies created for it in
// other SiteInstances have correct sandbox flags and origin.
//
//     A         A           A
//    /         / \         / \    .
//   B    ->   B   A   ->  B   A
//                              \  .
//                               B
//
// The test checks sandbox flags and origin for the proxy added in step 2, by
// checking whether the grandchild frame added in step 3 sees proper sandbox
// flags and origin for its (remote) parent.  This wasn't addressed when
// https://crbug.com/423587 was fixed.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       ProxiesForNewChildFramesHaveCorrectReplicationState) {}

// Verify that a child frame can retrieve the name property set by its parent.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, WindowNameReplication) {}

// Verify that dynamic updates to a frame's window.name propagate to the
// frame's proxies, so that the latest frame names can be used in navigations.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, DynamicWindowName) {}

// Verify that when a frame is navigated to a new origin, the origin update
// propagates to the frame's proxies.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, OriginUpdatesReachProxies) {}

// Ensure that navigating subframes in --site-per-process mode properly fires
// the DidStopLoading event on WebContentsObserver.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, CrossSiteDidStopLoading) {}

// Ensure that the renderer does not crash when navigating a frame that has a
// sibling RemoteFrame.  See https://crbug.com/426953.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       NavigateWithSiblingRemoteFrame) {}

// Ensure that the renderer does not crash when a local frame with a remote
// parent frame is swapped from local to remote, then back to local again.
// See https://crbug.com/585654.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       NavigateSiblingsToSameProcess) {}

// Verify that load events for iframe elements work when the child frame is
// out-of-process.  In such cases, the load event is forwarded from the child
// frame to the parent frame via the browser process.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, LoadEventForwarding) {}

// Check that postMessage can be routed between cross-site iframes.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, SubframePostMessage) {}

// Check that postMessage can be sent from a subframe on a cross-process opener
// tab, and that its event.source points to a valid proxy.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       PostMessageWithSubframeOnOpenerChain) {}

// Check that parent.frames[num] references correct sibling frames when the
// parent is remote.  See https://crbug.com/478792.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, IndexedFrameAccess) {}

IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, RFPHDestruction) {}

IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, OpenPopupWithRemoteParent) {}

// Test that cross-process popups can't be navigated to disallowed URLs by
// their opener.  This ensures that proper URL validation is performed when
// RenderFrameProxyHosts are navigated.  See https://crbug.com/595339.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, NavigatePopupToIllegalURL) {}

// Verify that named frames are discoverable from their opener's ancestors.
// See https://crbug.com/511474.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       DiscoverNamedFrameFromAncestorOfOpener) {}

class SitePerProcessFencedFrameTest : public SitePerProcessBrowserTestBase {};

IN_PROC_BROWSER_TEST_F(SitePerProcessFencedFrameTest,
                       PopupFromFencedFrameDoesNotCreateProxy) {}

// Similar to DiscoverNamedFrameFromAncestorOfOpener, but check that if a
// window is created without a name and acquires window.name later, it will
// still be discoverable from its opener's ancestors.  Also, instead of using
// an opener's ancestor, this test uses a popup with same origin as that
// ancestor. See https://crbug.com/511474.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       DiscoverFrameAfterSettingWindowName) {}

// Check that frame opener updates work with subframes.  Set up a window with a
// popup and update openers for the popup's main frame and subframe to
// subframes on first window, as follows:
//
//    foo      +---- bar
//    / \      |     / \      .
// bar   foo <-+  bar   foo
//  ^                    |
//  +--------------------+
//
// The sites are carefully set up so that both opener updates are cross-process
// but still allowed by Blink's navigation checks.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, UpdateSubframeOpener) {}

// Check that when a subframe navigates to a new SiteInstance, the new
// SiteInstance will get a proxy for the opener of subframe's parent.  I.e.,
// accessing parent.opener from the subframe should still work after a
// cross-process navigation.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       NavigatingSubframePreservesOpenerInParent) {}

// Check that if a subframe has an opener, that opener is preserved when the
// subframe navigates cross-site.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, NavigateSubframeWithOpener) {}

// Check that if a subframe has an opener, that opener is preserved when a new
// `blink::RemoteFrame` is created for that subframe in another renderer
// process. Similar to NavigateSubframeWithOpener, but this test verifies the
// subframe opener plumbing for blink::mojom::RemoteFrame::CreateRemoteChild(),
// whereas NavigateSubframeWithOpener targets mojom::Renderer::CreateFrame().
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       NewRenderFrameProxyPreservesOpener) {}

// Test for https://crbug.com/515302. Perform two navigations, A1 -> B2 -> A3,
// and drop the mojo::AgentSchedulingGroupHost::DidUnloadRenderFrame from the A1
// -> B2 navigation, so that the second B2 -> A3 navigation is initiated before
// the first page receives the
// mojo::AgentSchedulingGroupHost::DidUnloadRenderFrame. Ensure that this
// doesn't crash and that the RVH(A1) is not reused in that case.
#if BUILDFLAG(IS_MAC)
#define MAYBE_RenderViewHostIsNotReusedAfterDelayedUnloadACK
#else
#define MAYBE_RenderViewHostIsNotReusedAfterDelayedUnloadACK
#endif
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       MAYBE_RenderViewHostIsNotReusedAfterDelayedUnloadACK) {}

// Test for https://crbug.com/591478, where navigating to a cross-site page with
// a subframe on the old site caused a crash while trying to reuse the old
// RenderViewHost.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       ReusePendingDeleteRenderViewHostForSubframe) {}

// Check that when a cross-process frame acquires focus, the old focused frame
// loses focus and fires blur events.  Starting on a page with a cross-site
// subframe, simulate mouse clicks to switch focus from root frame to subframe
// and then back to root frame.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       CrossProcessFocusChangeFiresBlurEvents) {}

// Check that when a cross-process subframe is focused, its parent's
// document.activeElement correctly returns the corresponding <iframe> element.
// The test sets up an A-embed-B-embed-C page and shifts focus A->B->A->C,
// checking document.activeElement after each change.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, DocumentActiveElement) {}

// Check that window.focus works for cross-process subframes.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, SubframeWindowFocus) {}

// Check that when a subframe has focus, and another subframe navigates
// cross-site to a new renderer process, this doesn't reset the focused frame
// to the main frame.  See https://crbug.com/802156.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       SubframeFocusNotLostWhenAnotherFrameNavigatesCrossSite) {}

// Tests that we are using the correct `blink::RemoteFrame` when navigating an
// opener window.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, OpenerSetLocation) {}

// crbug.com/1281755
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
#define MAYBE_NavigateProxyAndDetachBeforeProvisionalFrameCreation
#else
#define MAYBE_NavigateProxyAndDetachBeforeProvisionalFrameCreation
#endif
// Test for https://crbug.com/526304, where a parent frame executes a
// remote-to-local navigation on a child frame and immediately removes the same
// child frame.  This test exercises the path where the detach happens before
// the provisional local frame is created.
IN_PROC_BROWSER_TEST_P(
    SitePerProcessBrowserTest,
    MAYBE_NavigateProxyAndDetachBeforeProvisionalFrameCreation) {}

// Test for a variation of https://crbug.com/526304, where a child frame does a
// remote-to-local navigation, and the parent frame removes that child frame
// after the provisional local frame is created and starts to navigate, but
// before it commits.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       NavigateProxyAndDetachBeforeCommit) {}

// Similar to NavigateProxyAndDetachBeforeCommit, but uses a synchronous
// navigation to about:blank and the parent removes the child frame in a load
// event handler for the subframe.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, NavigateAboutBlankAndDetach) {}

// This test ensures that the RenderFrame isn't leaked in the renderer process
// if a pending cross-process navigation is cancelled. The test works by trying
// to create a new RenderFrame with the same routing id. If there is an
// entry with the same routing ID, a CHECK is hit and the process crashes.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       SubframePendingAndBackToSameSiteInstance) {}

// This test ensures that the RenderFrame isn't leaked in the renderer process
// when a remote parent detaches a child frame. The test works by trying
// to create a new RenderFrame with the same routing id. If there is an
// entry with the same routing ID, a CHECK is hit and the process crashes.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, ParentDetachRemoteChild) {}

// Verify that sandbox flags inheritance works across multiple levels of
// frames.  See https://crbug.com/576845.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, SandboxFlagsInheritance) {}

// Check that sandbox flags are not inherited before they take effect.  Create
// a child frame, update its sandbox flags but don't navigate the frame, and
// ensure that a new cross-site grandchild frame doesn't inherit the new flags
// (which shouldn't have taken effect).
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       SandboxFlagsNotInheritedBeforeNavigation) {}

// Verify that popups opened from sandboxed frames inherit sandbox flags from
// their opener, and that they keep these inherited flags after being navigated
// cross-site.  See https://crbug.com/483584.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       NewPopupInheritsSandboxFlagsFromOpener) {}

// Verify that popups opened from frames sandboxed with the
// "allow-popups-to-escape-sandbox" directive do *not* inherit sandbox flags
// from their opener.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       OpenUnsandboxedPopupFromSandboxedFrame) {}

// Verify that popup frames opened from sandboxed documents with the
// "allow-popups-to-escape-sandbox" directive do *not* inherit sandbox flags AND
// that local scheme documents do *not* inherit flags from the opener/initiator.
IN_PROC_BROWSER_TEST_P(
    SitePerProcessBrowserTest,
    OpenSandboxedDocumentInUnsandboxedPopupFromSandboxedFrame) {}

// Verify that popup frames opened from sandboxed documents with the
// "allow-popups-to-escape-sandbox" directive do *not* inherit sandbox flags AND
// that local scheme documents do inherit CSP sandbox flags from the
// opener/initiator.
IN_PROC_BROWSER_TEST_P(
    SitePerProcessBrowserTest,
    OpenSandboxedDocumentInUnsandboxedPopupFromCSPSandboxedDocument) {}

// Test that subresources with certificate errors get reported to the
// browser. That is, if https://example.test frames https://a.com which
// loads an image with certificate errors, the browser should be
// notified about the subresource with certificate errors and downgrade
// the UI appropriately.
// TODO(crbug.com/40705650): Flaky.
IN_PROC_BROWSER_TEST_P(SitePerProcessIgnoreCertErrorsBrowserTest,
                       DISABLED_SubresourceWithCertificateErrors) {}

// Test setting a cross-origin iframe to display: none.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, CrossSiteIframeDisplayNone) {}

// Test that a cross-origin iframe can be blocked by X-Frame-Options and CSP
// frame-ancestors.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       CrossSiteIframeBlockedByXFrameOptionsOrCSP) {}

// Test that a cross-origin frame's navigation can be blocked by CSP frame-src.
// In this version of a test, CSP comes from HTTP headers.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       CrossSiteIframeBlockedByParentCSPFromHeaders) {}

// Test that a cross-origin frame's navigation can be blocked by CSP frame-src.
// In this version of a test, CSP comes from a <meta> element added after the
// page has already loaded.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       CrossSiteIframeBlockedByParentCSPFromMeta) {}

// Test that a cross-origin frame's navigation can be blocked by CSP frame-src.
// In this version of a test, CSP is inherited by srcdoc iframe from a parent
// that declared CSP via HTTP headers.  Cross-origin frame navigating to a
// blocked location is a child of the srcdoc iframe.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       CrossSiteIframeBlockedByCSPInheritedBySrcDocParent) {}

// Tests that the state of the RenderViewHost is properly reset when the main
// frame is navigated to the same SiteInstance as one of its child frames.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       NavigateMainFrameToChildSite) {}

// Test for https://crbug.com/568836.  From an A-embed-B page, navigate the
// subframe from B to A.  This cleans up the process for B, but the test delays
// the browser side from killing the B process right away.  This allows the
// B process to process the subframe's detached event and the disconnect
// of the blink::WebView's blink::mojom::PageBroadcast mojo channel. In the bug,
// the latter crashed while detaching the subframe's LocalFrame (triggered as
// part of closing the `blink::WebView`), because this tried to access the
// subframe's WebFrameWidget (from RenderFrameImpl::didChangeSelection), which
// had already been cleared by the former.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       CloseSubframeWidgetAndViewOnProcessExit) {}

// Tests that an input event targeted to a out-of-process iframe correctly
// triggers a user interaction notification for WebContentsObservers.
// This is used for browser features such as download request limiting and
// launching multiple external protocol handlers, which can block repeated
// actions from a page when a user is not interacting with the page.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       UserInteractionForChildFrameTest) {}

// Ensures that navigating to data: URLs present in session history will
// correctly commit the navigation in the same process as the one used for the
// original navigation. See https://crbug.com/606996.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       NavigateSubframeToDataUrlInSessionHistory) {}

// The site URL for a data: URL is the scheme + the serialized nonce from the
// origin. This means that two data: URLs with the same body will have different
// site URLs.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, DataUrlsHaveUniqueSiteURLs) {}

// Ensures that subframes navigated to data: URLs start in a process based on
// their creator, but end up in unique processes after a restore (since
// SiteInstance relationships are not preserved on restore, until
// https://crbug.com/14987 is fixed).  This is better than restoring into the
// parent process, per https://crbug.com/863069.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       SubframeDataUrlsAfterRestore) {}

// Similar to SubframeDataUrlsAfterRestore. Ensures that about:blank frames
// are not put into their parent process after restore if their initiator origin
// is different from the parent.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       SubframeBlankUrlsAfterRestore) {}

// Similar to SubframeBlankUrlsAfterRestore, but ensures that about:srcdoc ends
// up in its parent's process after restore, since that's where its content
// comes from.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       SubframeSrcdocUrlAfterRestore) {}

// Ensures that navigating to about:blank URLs present in session history will
// correctly commit the navigation in the same process as the one used for
// the original navigation.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       NavigateSubframeToAboutBlankInSessionHistory) {}

// Intercepts calls to LocalMainFrame's ShowCreatedWindow mojo method, and
// invokes the provided callback.
class ShowCreatedWindowInterceptor
    : public blink::mojom::LocalMainFrameHostInterceptorForTesting {};

// Listens for the source WebContents opening the new WebContents then attaches
// a show listener to the widget.
class NewWindowCreatedObserver : public WebContentsObserver {};

// Test for https://crbug.com/612276.  Simultaneously open two new windows from
// two subframes in different processes, where each subframe process's next
// routing ID is the same.  Make sure that both windows are created properly.
//
// Each new window requires two IPCs to first create it (handled by
// CreateNewWindow) and then show it (ShowCreatedWindow).  In the bug, both
// CreateNewWindow calls arrived before the ShowCreatedWindow calls, resulting
// in the two pending windows colliding in the pending WebContents map, which
// used to be keyed only by routing_id.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       TwoSubframesCreatePopupsSimultaneously) {}

// Intercepts calls to PopupWidgetHost's RequestClosePopup mojo method, and
// discards it. The caller has to guarantee that `render_widget_host` lives at
// least as long as RequestCloseWidgetInterceptor.
class RequestCloseWidgetInterceptor
    : public blink::mojom::PopupWidgetHostInterceptorForTesting {};

// Intercepts calls to PopupWidgetHost's ShowPopup mojo method, and
// invokes the provided callback. The caller has to guarantee that
// `render_widget_host` lives at least as long as
// ShowCreatedPopupWidgetInterceptor.
class ShowCreatedPopupWidgetInterceptor
    : public blink::mojom::PopupWidgetHostInterceptorForTesting {};

// Listens for the source RenderFrameHost opening the new popup widget then
// attaches a show listener to the widget.
class NewPopupWidgetCreatedObserver {};

// Test for https://crbug.com/612276.  Similar to
// TwoSubframesOpenWindowsSimultaneously, but use popup menu widgets instead of
// windows.
//
// The plumbing that this test is verifying is not utilized on Mac/Android,
// where popup menus don't create a popup RenderWidget, but rather they trigger
// a FrameHostMsg_ShowPopup to ask the browser to build and display the actual
// popup using native controls.
#if !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_ANDROID)
// Disable the test due to flaky: https://crbug.com/1126165
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
#define MAYBE_TwoSubframesCreatePopupMenuWidgetsSimultaneously
#else
#define MAYBE_TwoSubframesCreatePopupMenuWidgetsSimultaneously
#endif
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       MAYBE_TwoSubframesCreatePopupMenuWidgetsSimultaneously) {}
#endif

// Test for https://crbug.com/615575. It ensures that file chooser triggered
// by a document in an out-of-process subframe works properly.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, FileChooserInSubframe) {}

// Test that the pending RenderFrameHost is canceled and destroyed when its
// process dies. Previously, reusing a top-level pending RFH which
// is not live was hitting a CHECK in CreateRenderView due to having neither a
// main frame routing ID nor a proxy routing ID.  See https://crbug.com/627400
// for more details.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       PendingRFHIsCanceledWhenItsProcessDies) {}

// Test that killing a pending RenderFrameHost's process doesn't leave its
// RenderViewHost confused whether it's active or not for future navigations
// that try to reuse it.  See https://crbug.com/627893 for more details.
// Similar to the test above for https://crbug.com/627400, except the popup is
// navigated after pending RFH's process is killed, rather than the main tab.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       RenderViewHostKeepsSwappedOutStateIfPendingRFHDies) {}

// Test that a crashed subframe can be successfully navigated to the site it
// was on before crashing.  See https://crbug.com/634368.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       NavigateCrashedSubframeToSameSite) {}

// Test that session history length and offset are replicated to all renderer
// processes in a FrameTree.  This allows each renderer to see correct values
// for history.length, and to check the offset validity properly for
// navigations initiated via history.go(). See https:/crbug.com/501116.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, SessionHistoryReplication) {}

// Intercepts calls to LocalFrameHost::DispatchLoad method(), and discards them.
class DispatchLoadInterceptor
    : public blink::mojom::LocalFrameHostInterceptorForTesting {};

// Test that the renderer isn't killed when a frame generates a load event just
// after becoming pending deletion.  See https://crbug.com/636513.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       LoadEventForwardingWhilePendingDeletion) {}

IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       RFHTransfersWhilePendingDeletion) {}

class NavigationHandleWatcher : public WebContentsObserver {};

// Verifies that the SiteInstance of a NavigationHandle correctly identifies the
// RenderFrameHost that started the navigation (and not the destination RFH).
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       NavigationHandleSiteInstance) {}

// Test that when canceling a pending RenderFrameHost in the middle of a
// redirect, and then killing the corresponding `blink::WebView`'s renderer
// process, the RenderViewHost isn't reused in an improper state later.
// Previously this led to a crash in CreateRenderView when recreating the
// `blink::WebView` due to a stale main frame routing ID.  See
// https://crbug.com/627400.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       ReuseNonLiveRenderViewHostAfterCancelPending) {}

// Check that after a pending RFH is canceled and replaced with a proxy (which
// reuses the canceled RFH's RenderViewHost), navigating to a main frame in the
// same site as the canceled RFH doesn't lead to a renderer crash.  The steps
// here are similar to ReuseNonLiveRenderViewHostAfterCancelPending, but don't
// involve crashing the renderer. See https://crbug.com/651980.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       RecreateMainFrameAfterCancelPending) {}

// Check that when a pending RFH is canceled and a proxy needs to be created in
// its place, the proxy is properly initialized on the renderer side.  See
// https://crbug.com/653746.
// The test disables the delay of creating the speculative RFH since it requires
// the created RFH to be cancelld because of the cross-origin redirect.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTestWithoutSpeculativeRFHDelay,
                       CommunicateWithProxyAfterCancelPending) {}

IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       HeaderPolicyOnXSLTNavigation) {}

IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       TestPolicyReplicationOnSameOriginNavigation) {}

IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       TestPolicyReplicationOnCrossOriginNavigation) {}

// Test that the replicated permissions policy header is correct in subframes as
// they navigate.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       TestPolicyReplicationFromRemoteFrames) {}

// Test that the replicated permissions policy header is correct in remote
// proxies after the local frame has navigated.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       TestPermissionsPolicyReplicationToProxyOnNavigation) {}

// Test that the constructed permissions policy is correct in sandboxed
// frames. Sandboxed frames have an opaque origin, and if the frame policy,
// which is constructed in the parent frame, cannot send that origin through
// the browser process to the sandboxed frame, then the sandboxed frame's
// policy will be incorrect.
//
// This is a regression test for https://crbug.com/690520
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       TestAllowAttributeInSandboxedFrame) {}

// Test that the constructed permissions policy is correct in sandboxed
// frames. Sandboxed frames have an opaque origin, and if the frame policy,
// which is constructed in the parent frame, cannot send that origin through
// the browser process to the sandboxed frame, then the sandboxed frame's
// policy will be incorrect.
//
// This is a regression test for https://crbug.com/690520
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       TestAllowAttributeInOpaqueOriginAfterNavigation) {}

// Ensure that an iframe that navigates cross-site doesn't use the same process
// as its parent. Then when its parent navigates it via the "srcdoc" attribute,
// it must reuse its parent's process.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       IframeSrcdocAfterCrossSiteNavigation) {}

// Verify that a remote-to-local navigation in a crashed subframe works.  See
// https://crbug.com/487872.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       RemoteToLocalNavigationInCrashedSubframe) {}

// Tests that trying to open a context menu in the old RFH after commiting a
// navigation doesn't crash the browser. https://crbug.com/677266.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       ContextMenuAfterCrossProcessNavigation) {}

// Test iframe container policy is replicated properly to the browser.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, ContainerPolicy) {}

// Test dynamic updates to iframe "allow" attribute are propagated correctly.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, ContainerPolicyDynamic) {}

// Check that out-of-process frames correctly calculate the container policy in
// the renderer when navigating cross-origin. The policy should be unchanged
// when modified dynamically in the parent frame. When the frame is navigated,
// the new renderer should have the correct container policy.
//
// TODO(iclelland): Once there is a proper JS inspection API from the renderer,
// use that to check the policy. Until then, we test webkitFullscreenEnabled,
// which conveniently just returns the result of calling isFeatureEnabled on
// the fullscreen feature. Since there are no HTTP header policies involved,
// this verifies the presence of the container policy in the iframe.
// https://crbug.com/703703
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       ContainerPolicyCrossOriginNavigation) {}

// Test that dynamic updates to iframe sandbox attribute correctly set the
// replicated container policy.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       ContainerPolicySandboxDynamic) {}

// Test that creating a new remote frame at the same origin as its parent
// results in the correct permissions policy in the RemoteSecurityContext.
// https://crbug.com/852102
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       PermissionsPolicyConstructionInExistingProxy) {}

// Test harness that allows for "barrier" style delaying of requests matching
// certain paths. Call SetDelayedRequestsForPath to delay requests, then
// SetUpEmbeddedTestServer to register handlers and start the server.
class RequestDelayingSitePerProcessBrowserTest
    : public SitePerProcessBrowserTest {};

// Regression tests for https://crbug.com/678206, where the request throttling
// in ResourceScheduler was not updated for OOPIFs. This resulted in a single
// hung delayable request (e.g. video) starving all other delayable requests.
// The tests work by delaying n requests in a cross-domain iframe. Once the n +
// 1st request goes through to the network stack (ensuring it was not starved),
// the delayed request completed.
//
// If the logic is not correct, these tests will time out, as the n + 1st
// request will never start.
IN_PROC_BROWSER_TEST_P(RequestDelayingSitePerProcessBrowserTest,
                       DelayableSubframeRequestsOneFrame) {}

IN_PROC_BROWSER_TEST_P(RequestDelayingSitePerProcessBrowserTest,
                       DelayableSubframeRequestsTwoFrames) {}

#if BUILDFLAG(IS_ANDROID)
class TextSelectionObserver : public TextInputManager::Observer {
 public:
  explicit TextSelectionObserver(TextInputManager* text_input_manager)
      : text_input_manager_(text_input_manager) {
    text_input_manager->AddObserver(this);
  }

  TextSelectionObserver(const TextSelectionObserver&) = delete;
  TextSelectionObserver& operator=(const TextSelectionObserver&) = delete;

  ~TextSelectionObserver() { text_input_manager_->RemoveObserver(this); }

  void WaitForSelectedText(const std::string& expected_text) {
    if (last_selected_text_ == expected_text)
      return;
    expected_text_ = expected_text;
    loop_runner_ = new MessageLoopRunner();
    loop_runner_->Run();
  }

 private:
  void OnTextSelectionChanged(TextInputManager* text_input_manager,
                              RenderWidgetHostViewBase* updated_view) override {
    last_selected_text_ = base::UTF16ToUTF8(
        text_input_manager->GetTextSelection(updated_view)->selected_text());
    if (last_selected_text_ == expected_text_ && loop_runner_)
      loop_runner_->Quit();
  }

  const raw_ptr<TextInputManager> text_input_manager_;
  std::string last_selected_text_;
  std::string expected_text_;
  scoped_refptr<MessageLoopRunner> loop_runner_;
};

class SitePerProcessAndroidImeTest : public SitePerProcessBrowserTest {
 public:
  SitePerProcessAndroidImeTest() : SitePerProcessBrowserTest() {}

  SitePerProcessAndroidImeTest(const SitePerProcessAndroidImeTest&) = delete;
  SitePerProcessAndroidImeTest& operator=(const SitePerProcessAndroidImeTest&) =
      delete;

  ~SitePerProcessAndroidImeTest() override {}

 protected:
  ImeAdapterAndroid* ime_adapter() {
    return static_cast<RenderWidgetHostViewAndroid*>(
               web_contents()->GetRenderWidgetHostView())
        ->ime_adapter_for_testing();
  }

  void FocusInputInFrame(RenderFrameHostImpl* frame) {
    ASSERT_TRUE(ExecJs(frame, "window.focus(); input.focus();"));
  }

  // Creates a page with multiple (nested) OOPIFs and populates all of them
  // with an <input> element along with the required handlers for the test.
  void LoadPage() {
    ASSERT_TRUE(NavigateToURL(
        shell(),
        GURL(embedded_test_server()->GetURL(
            "a.com", "/cross_site_iframe_factory.html?a(b,c(a(b)))"))));
    FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
    frames_.push_back(root->current_frame_host());
    frames_.push_back(root->child_at(0)->current_frame_host());
    frames_.push_back(root->child_at(1)->current_frame_host());
    frames_.push_back(root->child_at(1)->child_at(0)->current_frame_host());
    frames_.push_back(
        root->child_at(1)->child_at(0)->child_at(0)->current_frame_host());

    // Adds an <input> to frame and sets up a handler for |window.oninput|. When
    // the input event is fired (by changing the value of <input> element), the
    // handler will select all the text so that the corresponding text selection
    // update on the browser side notifies the test about input insertion.
    std::string add_input_script =
        "var input = document.createElement('input');"
        "document.body.appendChild(input);"
        "window.oninput = function() {"
        "  input.select();"
        "};";

    for (content::RenderFrameHostImpl* frame : frames_) {
      ASSERT_TRUE(ExecJs(frame, add_input_script));
    }
  }

  // This methods tries to commit |text| by simulating a native call from Java.
  void CommitText(const char* text) {
    JNIEnv* env = base::android::AttachCurrentThread();

    // A valid caller is needed for ImeAdapterAndroid::GetUnderlinesFromSpans.
    base::android::ScopedJavaLocalRef<jobject> caller =
        ime_adapter()->java_ime_adapter_for_testing(env);

    // Input string from Java side.
    base::android::ScopedJavaLocalRef<jstring> jtext =
        base::android::ConvertUTF8ToJavaString(env, text);

    // Simulating a native call from Java side.
    ime_adapter()->CommitText(
        env, base::android::JavaParamRef<jobject>(env, caller.obj()),
        base::android::JavaParamRef<jobject>(env, jtext.obj()),
        base::android::JavaParamRef<jstring>(env, jtext.obj()), 0);
  }

  std::vector<raw_ptr<RenderFrameHostImpl, VectorExperimental>> frames_;
};

// This test verifies that committing text will be applied on the focused
// RenderWidgetHost.
IN_PROC_BROWSER_TEST_P(SitePerProcessAndroidImeTest,
                       CommitTextForFocusedWidget) {
  LoadPage();
  TextSelectionObserver selection_observer(
      web_contents()->GetTextInputManager());
  for (size_t index = 0; index < frames_.size(); ++index) {
    std::string text = base::StringPrintf("text%zu", index);
    FocusInputInFrame(frames_[index]);
    CommitText(text.c_str());
    selection_observer.WaitForSelectedText(text);
  }
}
#endif  // BUILDFLAG(IS_ANDROID)

// Test that an OOPIF at b.com can navigate to a cross-site a.com URL that
// transfers back to b.com.  See https://crbug.com/681077#c10 and
// https://crbug.com/660407.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       SubframeTransfersToCurrentRFH) {}

IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       FrameSwapPreservesUniqueName) {}

// Tests that POST body is not lost when it targets a OOPIF.
// See https://crbug.com/710937.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, PostTargetSubFrame) {}

// Tests that POST method and body is not lost when an OOPIF submits a form
// that targets the main frame.  See https://crbug.com/806215.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       PostTargetsMainFrameFromOOPIF) {}

// Verify that a remote-to-local main frame navigation doesn't overwrite
// the previous history entry.  See https://crbug.com/725716.
IN_PROC_BROWSER_TEST_P(
    SitePerProcessBrowserTest,
    DISABLED_CrossProcessMainFrameNavigationDoesNotOverwriteHistory) {}

// The test is flaky on Linux, Chrome OS, etc; cf https://crbug.com/1170583.
#if BUILDFLAG(IS_POSIX)
#define MAYBE_CrossProcessInertSubframe
#else
#define MAYBE_CrossProcessInertSubframe
#endif
// Tests that when an out-of-process iframe becomes inert due to a modal
// <dialog> element, the contents of the iframe can still take focus.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       MAYBE_CrossProcessInertSubframe) {}

// Tests that IsInert frame flag is correctly updated and propagated.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       CrossProcessIsInertPropagation) {}

// Check that main frames for the same site rendering in unrelated tabs start
// sharing processes that are already dedicated to that site when over process
// limit. See https://crbug.com/513036.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       MainFrameProcessReuseWhenOverLimit) {}

// Check that subframes for the same site rendering in unrelated tabs start
// sharing processes that are already dedicated to that site when over process
// limit. See https://crbug.com/513036.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       SubframeProcessReuseWhenOverLimit) {}

// Check that when a main frame and a subframe start navigating to the same
// cross-site URL at the same time, the new RenderFrame for the subframe is
// created successfully without crashing, and the navigations complete
// successfully.  This test checks the scenario where the main frame ends up
// committing before the subframe, and the test below checks the case where the
// subframe commits first.
//
// This used to be problematic in that the main frame navigation created an
// active RenderViewHost with a RenderFrame already swapped into the tree, and
// then while that navigation was still pending, the subframe navigation
// created its RenderFrame, which crashed when referencing its parent by a
// proxy which didn't exist.
//
// All cross-process navigations now require creating a `blink::RemoteFrame`
// before creating a RenderFrame, which makes such navigations follow the
// provisional frame (remote-to-local navigation) paths, where such a scenario
// is no longer possible.  See https://crbug.com/756790.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       TwoCrossSitePendingNavigationsAndMainFrameWins) {}

// Similar to TwoCrossSitePendingNavigationsAndMainFrameWins, but checks the
// case where the subframe navigation commits before the main frame.  See
// https://crbug.com/756790.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       TwoCrossSitePendingNavigationsAndSubframeWins) {}

// Similar to TwoCrossSitePendingNavigations* tests above, but checks the case
// where the current window and its opener navigate simultaneously.
// See https://crbug.com/756790.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       TwoCrossSitePendingNavigationsWithOpener) {}

IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       DetachSpeculativeRenderFrameHost) {}

// Tests what happens if the renderer attempts to cancel a navigation after the
// NavigationRequest has already reached READY_TO_COMMIT.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       CancelNavigationAfterReadyToCommit) {}

namespace {

// Helper for various <object> navigation test cases that trigger fallback
// handling. Fallback handling should never reach ready-to-commit navigation, so
// this helper forces test failure if a ReadyToCommitNavigation() is received.
class AssertNoReadyToCommitNavigationCalls : public WebContentsObserver {};

}  // namespace

// Test that a same-site navigation in <object> that fails with an HTTP error
// directly triggers fallback handling, rather than triggering fallback handling
// in the renderer after it receives a `CommitNavigation()` IPC.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       ObjectTagSameSiteNavigationWithHTTPError) {}

// Test that a cross-site navigation in <object> that fails with an HTTP error
// directly triggers fallback handling, rather than triggering fallback handling
// in the renderer after it receives a `CommitNavigation()` IPC.
// The test disables the delay of creating the speculative RFH since it
// will check the created speculative RFH for a failing request. The speculative
// RFH will not be created after receiving the 404 response.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTestWithoutSpeculativeRFHDelay,
                       ObjectTagCrossSiteNavigationWithHTTPError) {}

// Test that a same-site navigation in <object> that fails with an HTTP error
// and also subsequently fails to load the body still directly triggers fallback
// handling, rather than triggering fallback handling in the renderer after it
// receives a `CommitNavigation()` IPC.
IN_PROC_BROWSER_TEST_P(
    SitePerProcessBrowserTest,
    ObjectTagSameSiteNavigationWithHTTPErrorAndFailedBodyLoad) {}

// Test that a cross-site navigation in <object> that fails with an HTTP error
// and also subsequently fails to load the body still directly triggers fallback
// handling, rather than triggering fallback handling in the renderer after it
// receives a `CommitNavigation()` IPC.
// The test disables the delay of creating the speculative RFH since it
// will check the created speculative RFH for a failing request. The speculative
// RFH will not be created after receiving the 404 response.
IN_PROC_BROWSER_TEST_P(
    SitePerProcessBrowserTestWithoutSpeculativeRFHDelay,
    ObjectTagCrossSiteNavigationWithHTTPErrorAndFailedBodyLoad) {}

// Test that a same-site navigation in <object> that fails with a network error
// directly triggers fallback handling, rather than triggering fallback handling
// in the renderer after it receives a `CommitFailedNavigation()` IPC.
// The test disables the delay of creating the speculative RFH since it
// will check the created speculative RFH for a failing request. The speculative
// RFH will not be created after the network error.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTestWithoutSpeculativeRFHDelay,
                       ObjectTagSameSiteNavigationWithNetworkError) {}

// Test that a cross-site navigation in <object> that fails with a network error
// directly triggers fallback handling, rather than triggering fallback handling
// in the renderer after it receives a `CommitFailedNavigation()` IPC.
// The test disables the delay of creating the speculative RFH since it
// will check the created speculative RFH for a failing request. The speculative
// RFH will not be created after the network error.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTestWithoutSpeculativeRFHDelay,
                       ObjectTagCrossSiteNavigationWithNetworkError) {}

class SitePerProcessBrowserTestWithLeakDetector
    : public SitePerProcessBrowserTest {};

IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTestWithLeakDetector,
                       CloseWebContentsWithSpeculativeRenderFrameHost) {}

IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTestWithLeakDetector,
                       DetachFrameWithSpeculativeRenderFrameHost) {}

#if BUILDFLAG(IS_ANDROID)

namespace {

class MockEventHandlerAndroid : public ui::EventHandlerAndroid {
 public:
  bool OnTouchEvent(const ui::MotionEventAndroid& event) override {
    did_receive_event_ = true;
    return true;
  }

  bool did_receive_event() { return did_receive_event_; }

 private:
  bool did_receive_event_ = false;
};

}  // namespace

IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       SpeculativeRenderFrameHostDoesNotReceiveInput) {
  GURL url1(embedded_test_server()->GetURL("a.com", "/title1.html"));
  EXPECT_TRUE(NavigateToURL(shell(), url1));

  RenderWidgetHostViewAndroid* rwhva =
      static_cast<RenderWidgetHostViewAndroid*>(
          shell()->web_contents()->GetRenderWidgetHostView());
  ui::ViewAndroid* rwhva_native_view = rwhva->GetNativeView();
  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();

  // Start a cross-site navigation.
  GURL url2(embedded_test_server()->GetURL("b.com", "/title2.html"));
  TestNavigationManager nav_manager(web_contents(), url2);
  shell()->LoadURL(url2);

  // Wait for the request, but don't commit it yet. This should create a
  // speculative RenderFrameHost.
  nav_manager.WaitForSpeculativeRenderFrameHostCreation();
  RenderFrameHostImpl* root_speculative_rfh =
      root->render_manager()->speculative_frame_host();
  EXPECT_TRUE(root_speculative_rfh);
  RenderWidgetHostViewAndroid* rwhv_speculative =
      static_cast<RenderWidgetHostViewAndroid*>(
          root_speculative_rfh->GetView());
  ui::ViewAndroid* rwhv_speculative_native_view =
      rwhv_speculative->GetNativeView();

  ui::ViewAndroid* root_view = web_contents()->GetView()->GetNativeView();
  EXPECT_TRUE(root_view);

  MockEventHandlerAndroid mock_handler;
  rwhva_native_view->set_event_handler(&mock_handler);
  MockEventHandlerAndroid mock_handler_speculative;
  rwhv_speculative_native_view->set_event_handler(&mock_handler_speculative);
  // Avoid having the root try to handle the following event.
  root_view->set_event_handler(nullptr);

  auto size = root_view->GetSize();
  float x = size.width() / 2;
  float y = size.height() / 2;
  ui::MotionEventAndroid::Pointer pointer0(0, x, y, 0, 0, 0, 0, 0);
  ui::MotionEventAndroid::Pointer pointer1(0, 0, 0, 0, 0, 0, 0, 0);
  ui::MotionEventAndroid event(nullptr, nullptr, 1.f / root_view->GetDipScale(),
                               0.f, 0.f, 0.f, base::TimeTicks(), 0, 1, 0, 0, 0,
                               0, 0, 0, 0, 0, false, &pointer0, &pointer1);
  root_view->OnTouchEventForTesting(event);

  EXPECT_TRUE(mock_handler.did_receive_event());
  EXPECT_FALSE(mock_handler_speculative.did_receive_event());
}

IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, TestChildProcessImportance) {
  web_contents()->SetPrimaryMainFrameImportance(
      ChildProcessImportance::MODERATE);

  // Construct root page with one child in different domain.
  GURL main_url(embedded_test_server()->GetURL(
      "a.com", "/cross_site_iframe_factory.html?a(b)"));
  EXPECT_TRUE(NavigateToURL(shell(), main_url));
  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
  ASSERT_EQ(1u, root->child_count());
  FrameTreeNode* child = root->child_at(0);

  // Importance should survive initial navigation. Note importance only affect
  // main frame, so sub frame process should remain NORMAL throughout.
  EXPECT_EQ(ChildProcessImportance::MODERATE,
            root->current_frame_host()->GetProcess()->GetEffectiveImportance());
  EXPECT_EQ(
      ChildProcessImportance::NORMAL,
      child->current_frame_host()->GetProcess()->GetEffectiveImportance());

  // Check setting importance.
  web_contents()->SetPrimaryMainFrameImportance(ChildProcessImportance::NORMAL);
  EXPECT_EQ(ChildProcessImportance::NORMAL,
            root->current_frame_host()->GetProcess()->GetEffectiveImportance());
  EXPECT_EQ(
      ChildProcessImportance::NORMAL,
      child->current_frame_host()->GetProcess()->GetEffectiveImportance());
  web_contents()->SetPrimaryMainFrameImportance(
      ChildProcessImportance::IMPORTANT);
  EXPECT_EQ(ChildProcessImportance::IMPORTANT,
            root->current_frame_host()->GetProcess()->GetEffectiveImportance());
  EXPECT_EQ(
      ChildProcessImportance::NORMAL,
      child->current_frame_host()->GetProcess()->GetEffectiveImportance());

  // Check importance is maintained if child navigates to new domain.
  int old_child_process_id = child->current_frame_host()->GetProcess()->GetID();
  GURL url = embedded_test_server()->GetURL("foo.com", "/title2.html");
  EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), url));
  int new_child_process_id = child->current_frame_host()->GetProcess()->GetID();
  EXPECT_NE(old_child_process_id, new_child_process_id);
  EXPECT_EQ(
      ChildProcessImportance::NORMAL,
      child->current_frame_host()->GetProcess()->GetEffectiveImportance());
  EXPECT_EQ(ChildProcessImportance::IMPORTANT,
            root->current_frame_host()->GetProcess()->GetEffectiveImportance());

  // Check importance is maintained if root navigates to new domain.
  int old_root_process_id = root->current_frame_host()->GetProcess()->GetID();
  child = nullptr;  // Going to navigate root to page without any child.
  EXPECT_TRUE(NavigateToURLFromRenderer(root, url));
  EXPECT_EQ(0u, root->child_count());
  int new_root_process_id = root->current_frame_host()->GetProcess()->GetID();
  EXPECT_NE(old_root_process_id, new_root_process_id);
  EXPECT_EQ(ChildProcessImportance::IMPORTANT,
            root->current_frame_host()->GetProcess()->GetEffectiveImportance());
}

class TouchSelectionControllerClientTestWrapper
    : public ui::TouchSelectionControllerClient {
 public:
  explicit TouchSelectionControllerClientTestWrapper(
      ui::TouchSelectionControllerClient* client)
      : expected_event_(ui::SELECTION_HANDLES_SHOWN), client_(client) {}

  TouchSelectionControllerClientTestWrapper(
      const TouchSelectionControllerClientTestWrapper&) = delete;
  TouchSelectionControllerClientTestWrapper& operator=(
      const TouchSelectionControllerClientTestWrapper&) = delete;

  ~TouchSelectionControllerClientTestWrapper() override {}

  void InitWaitForSelectionEvent(ui::SelectionEventType expected_event) {
    DCHECK(!run_loop_);
    expected_event_ = expected_event;
    run_loop_ = std::make_unique<base::RunLoop>();
  }

  void Wait() {
    DCHECK(run_loop_);
    run_loop_->Run();
    run_loop_.reset();
  }

 private:
  // TouchSelectionControllerClient:
  void OnSelectionEvent(ui::SelectionEventType event) override {
    client_->OnSelectionEvent(event);
    if (run_loop_ && event == expected_event_)
      run_loop_->Quit();
  }

  bool SupportsAnimation() const override {
    return client_->SupportsAnimation();
  }

  void SetNeedsAnimate() override { client_->SetNeedsAnimate(); }

  void MoveCaret(const gfx::PointF& position) override {
    client_->MoveCaret(position);
  }

  void MoveRangeSelectionExtent(const gfx::PointF& extent) override {
    client_->MoveRangeSelectionExtent(extent);
  }

  void SelectBetweenCoordinates(const gfx::PointF& base,
                                const gfx::PointF& extent) override {
    client_->SelectBetweenCoordinates(base, extent);
  }

  std::unique_ptr<ui::TouchHandleDrawable> CreateDrawable() override {
    return client_->CreateDrawable();
  }

  void DidScroll() override {}

  void OnDragUpdate(const ui::TouchSelectionDraggable::Type type,
                    const gfx::PointF& position) override {}

  ui::SelectionEventType expected_event_;
  std::unique_ptr<base::RunLoop> run_loop_;
  // Not owned.
  raw_ptr<ui::TouchSelectionControllerClient, DanglingUntriaged> client_;
};

class TouchSelectionControllerClientAndroidSiteIsolationTest
    : public SitePerProcessBrowserTest {
 public:
  TouchSelectionControllerClientAndroidSiteIsolationTest()
      : root_rwhv_(nullptr),
        child_rwhv_(nullptr),
        child_frame_tree_node_(nullptr),
        selection_controller_client_(nullptr) {}

  void SetUpCommandLine(base::CommandLine* command_line) override {
    SitePerProcessBrowserTestBase::SetUpCommandLine(command_line);
    IsolateAllSitesForTesting(command_line);
  }

  RenderWidgetHostViewAndroid* GetRenderWidgetHostViewAndroid() {
    return static_cast<RenderWidgetHostViewAndroid*>(
        shell()->web_contents()->GetRenderWidgetHostView());
  }

  void SelectWithLongPress(gfx::Point point) {
    // Get main frame view for event insertion.
    RenderWidgetHostViewAndroid* main_view = GetRenderWidgetHostViewAndroid();

    SendTouch(main_view, ui::MotionEvent::Action::DOWN, point);
    // action_timeout() is far longer than needed for a LongPress, so we use
    // a custom timeout here.
    DelayBy(base::Milliseconds(2000));
    SendTouch(main_view, ui::MotionEvent::Action::UP, point);
  }

  void SimpleTap(gfx::Point point) {
    // Get main frame view for event insertion.
    RenderWidgetHostViewAndroid* main_view = GetRenderWidgetHostViewAndroid();

    SendTouch(main_view, ui::MotionEvent::Action::DOWN, point);
    // tiny_timeout() is way shorter than a reasonable user-created tap gesture,
    // so we use a custom timeout here.
    DelayBy(base::Milliseconds(300));
    SendTouch(main_view, ui::MotionEvent::Action::UP, point);
  }

  void SetupTest() {
    GURL test_url(embedded_test_server()->GetURL(
        "a.com", "/cross_site_iframe_factory.html?a(a)"));
    EXPECT_TRUE(NavigateToURL(shell(), test_url));
    frame_observer_ = std::make_unique<RenderFrameSubmissionObserver>(
        shell()->web_contents());
    FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
                              ->GetPrimaryFrameTree()
                              .root();
    EXPECT_EQ(
        " Site A\n"
        "   +--Site A\n"
        "Where A = http://a.com/",
        FrameTreeVisualizer().DepictFrameTree(root));
    TestNavigationObserver observer(shell()->web_contents());
    EXPECT_EQ(1u, root->child_count());
    child_frame_tree_node_ = root->child_at(0);

    root_rwhv_ = static_cast<RenderWidgetHostViewAndroid*>(
        root->current_frame_host()->GetRenderWidgetHost()->GetView());
    selection_controller_client_ =
        new TouchSelectionControllerClientTestWrapper(
            root_rwhv_->GetSelectionControllerClientManagerForTesting());
    root_rwhv_->SetSelectionControllerClientForTesting(
        base::WrapUnique(selection_controller_client_.get()));

    // We need to load the desired subframe and then wait until it's stable,
    // i.e. generates no new compositor frames for some reasonable time period:
    // a stray frame between touch selection's pre-handling of GestureLongPress
    // and the expected frame containing the selected region can confuse the
    // TouchSelectionController, causing it to fail to show selection handles.
    // Note this is an issue with the TouchSelectionController in general, and
    // not a property of this test.
    GURL child_url(
        embedded_test_server()->GetURL("b.com", "/touch_selection.html"));
    EXPECT_TRUE(
        NavigateToURLFromRenderer(child_frame_tree_node_.get(), child_url));
    EXPECT_EQ(
        " Site A ------------ proxies for B\n"
        "   +--Site B ------- proxies for A\n"
        "Where A = http://a.com/\n"
        "      B = http://b.com/",
        FrameTreeVisualizer().DepictFrameTree(root));
    // The child will change with the cross-site navigation. It shouldn't change
    // after this.
    child_frame_tree_node_ = root->child_at(0);
    WaitForHitTestData(child_frame_tree_node_->current_frame_host());

    child_rwhv_ = static_cast<RenderWidgetHostViewChildFrame*>(
        child_frame_tree_node_->current_frame_host()
            ->GetRenderWidgetHost()
            ->GetView());

    EXPECT_EQ(child_url, observer.last_navigation_url());
    EXPECT_TRUE(observer.last_navigation_succeeded());
  }

  // This must be called before the main-frame's RenderWidgetHostView is freed,
  // else we'll have a nullptr dereference on shutdown.
  void ShutdownTest() {
    ASSERT_TRUE(frame_observer_);
    frame_observer_.reset();
  }

  gfx::PointF GetPointInChild() {
    gfx::PointF point_f;
    std::string str = EvalJs(child_frame_tree_node_->current_frame_host(),
                             "get_top_left_of_text()")
                          .ExtractString();
    ConvertJSONToPoint(str, &point_f);
    // Offset the point so that it is within the text. Character dimensions are
    // based on the font size in `touch_selection.html`.
    constexpr int kCharacterWidth = 15;
    constexpr int kCharacterHeight = 15;
    point_f.Offset(2 * kCharacterWidth, 0.5f * kCharacterHeight);
    point_f = child_rwhv()->TransformPointToRootCoordSpaceF(point_f);
    return point_f;
  }

  void VerifyHandlePosition() {
    // Check that selection handles are close to the selection range.
    // The test will timeout if this never happens.
    ui::TouchSelectionController* touch_selection_controller =
        root_rwhv()->touch_selection_controller();

    bool handles_in_place = false;
    while (!handles_in_place) {
      gfx::PointF selection_start =
          touch_selection_controller->GetStartPosition();
      gfx::PointF selection_end = touch_selection_controller->GetEndPosition();
      gfx::RectF handle_start =
          touch_selection_controller->GetStartHandleRect();
      gfx::RectF handle_end = touch_selection_controller->GetEndHandleRect();

      // Not all Android bots seem to actually show the handle, so check first.
      if (handle_start.IsEmpty()) {
        handles_in_place = true;
      } else {
        bool has_end_handle =
            !touch_selection_controller->GetEndHandleRect().IsEmpty();
        // handle_start.y() defined the top of the handle's rect, and x() is
        // left.
        bool start_near_y =
            std::abs(selection_start.y() - handle_start.y()) <= 3.f;
        bool start_in_x_range = selection_start.x() >= handle_start.x() &&
                                selection_start.x() <= handle_start.right();
        bool end_near_y = std::abs(selection_end.y() - handle_end.y()) <= 3.f;
        bool end_in_x_range = selection_end.x() >= handle_end.x() &&
                              selection_end.x() <= handle_end.right();
        handles_in_place = start_near_y && start_in_x_range && end_near_y &&
                           end_in_x_range && has_end_handle;
      }
      if (!handles_in_place)
        DelayBy(base::Milliseconds(100));
    }
  }

  RenderWidgetHostViewAndroid* root_rwhv() { return root_rwhv_; }

  RenderWidgetHostViewChildFrame* child_rwhv() { return child_rwhv_; }

  float PageScaleFactor() {
    return frame_observer_->LastRenderFrameMetadata().page_scale_factor;
  }

  TouchSelectionControllerClientTestWrapper* selection_controller_client() {
    return selection_controller_client_;
  }

  void OnSyntheticGestureSent() {
    gesture_run_loop_ = std::make_unique<base::RunLoop>();
    gesture_run_loop_->Run();
  }

  void OnSyntheticGestureCompleted(SyntheticGesture::Result result) {
    EXPECT_EQ(SyntheticGesture::GESTURE_FINISHED, result);
    gesture_run_loop_->Quit();
  }

 protected:
  void DelayBy(base::TimeDelta delta) {
    base::RunLoop run_loop;
    base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
        FROM_HERE, run_loop.QuitClosure(), delta);
    run_loop.Run();
  }

 private:
  void SendTouch(RenderWidgetHostViewAndroid* view,
                 ui::MotionEvent::Action action,
                 gfx::Point point) {
    DCHECK(action >= ui::MotionEvent::Action::DOWN &&
           action < ui::MotionEvent::Action::CANCEL);

    ui::MotionEventAndroid::Pointer p(0, point.x(), point.y(), 10, 0, 0, 0, 0);
    JNIEnv* env = base::android::AttachCurrentThread();
    auto time_ns = (ui::EventTimeForNow() - base::TimeTicks()).InNanoseconds();
    ui::MotionEventAndroid touch(
        env, nullptr, 1.f, 0, 0, 0, base::TimeTicks::FromJavaNanoTime(time_ns),
        ui::MotionEventAndroid::GetAndroidAction(action), 1, 0, 0, 0, 0, 0, 0,
        0, 0, false, &p, nullptr);
    view->OnTouchEvent(touch);
  }

  raw_ptr<RenderWidgetHostViewAndroid, DanglingUntriaged> root_rwhv_;
  raw_ptr<RenderWidgetHostViewChildFrame, DanglingUntriaged> child_rwhv_;
  raw_ptr<FrameTreeNode, DanglingUntriaged> child_frame_tree_node_;
  std::unique_ptr<RenderFrameSubmissionObserver> frame_observer_;
  raw_ptr<TouchSelectionControllerClientTestWrapper, DanglingUntriaged>
      selection_controller_client_;

  std::unique_ptr<base::RunLoop> gesture_run_loop_;
};

IN_PROC_BROWSER_TEST_P(TouchSelectionControllerClientAndroidSiteIsolationTest,
                       BasicSelectionIsolatedIframe) {
  // Load test URL with cross-process child.
  SetupTest();

  EXPECT_EQ(ui::TouchSelectionController::INACTIVE,
            root_rwhv()->touch_selection_controller()->active_status());
  // Find the location of some text to select.
  gfx::PointF point_f = GetPointInChild();

  // Initiate selection with a sequence of events that go through the targeting
  // system.
  selection_controller_client()->InitWaitForSelectionEvent(
      ui::SELECTION_HANDLES_SHOWN);

  SelectWithLongPress(gfx::Point(point_f.x(), point_f.y()));

  selection_controller_client()->Wait();

  // Check that selection is active and the quick menu is showing.
  EXPECT_EQ(ui::TouchSelectionController::SELECTION_ACTIVE,
            root_rwhv()->touch_selection_controller()->active_status());

  // Make sure handles are correctly positioned.
  VerifyHandlePosition();

  // Tap inside/outside the iframe and make sure the selection handles go away.
  selection_controller_client()->InitWaitForSelectionEvent(
      ui::SELECTION_HANDLES_CLEARED);
  // Since Android tests may run with page_scale_factor < 1, use an offset a
  // bigger than +/-1 for doing the inside/outside taps to cancel the selection
  // handles.
  gfx::PointF point_inside_iframe =
      child_rwhv()->TransformPointToRootCoordSpaceF(gfx::PointF(+5.f, +5.f));
  SimpleTap(gfx::Point(point_inside_iframe.x(), point_inside_iframe.y()));
  selection_controller_client()->Wait();

  EXPECT_EQ(ui::TouchSelectionController::INACTIVE,
            root_rwhv()->touch_selection_controller()->active_status());

  // Let's wait for the previous events to clear the round-trip to the renders
  // and back.
  DelayBy(base::Milliseconds(2000));

  // Initiate selection with a sequence of events that go through the targeting
  // system. Repeat of above but this time we'l cancel the selection by
  // tapping outside of the OOPIF.
  selection_controller_client()->InitWaitForSelectionEvent(
      ui::SELECTION_HANDLES_SHOWN);

  SelectWithLongPress(gfx::Point(point_f.x(), point_f.y()));

  selection_controller_client()->Wait();

  // Check that selection is active and the quick menu is showing.
  EXPECT_EQ(ui::TouchSelectionController::SELECTION_ACTIVE,
            root_rwhv()->touch_selection_controller()->active_status());

  // Tap inside/outside the iframe and make sure the selection handles go away.
  selection_controller_client()->InitWaitForSelectionEvent(
      ui::SELECTION_HANDLES_CLEARED);
  // Since Android tests may run with page_scale_factor < 1, use an offset a
  // bigger than +/-1 for doing the inside/outside taps to cancel the selection
  // handles.
  gfx::PointF point_outside_iframe =
      child_rwhv()->TransformPointToRootCoordSpaceF(gfx::PointF(-5.f, -5.f));
  SimpleTap(gfx::Point(point_outside_iframe.x(), point_outside_iframe.y()));
  selection_controller_client()->Wait();

  EXPECT_EQ(ui::TouchSelectionController::INACTIVE,
            root_rwhv()->touch_selection_controller()->active_status());

  // Cleanup before shutting down.
  ShutdownTest();
}

// This test verifies that the handles associated with an active touch selection
// are still correctly positioned after a pinch-zoom operation.
#if BUILDFLAG(IS_ANDROID)  // Flaky on Android.  See https://crbug.com/906204.
#define MAYBE_SelectionThenPinchInOOPIF
#else
#define MAYBE_SelectionThenPinchInOOPIF
#endif
IN_PROC_BROWSER_TEST_P(TouchSelectionControllerClientAndroidSiteIsolationTest,
                       MAYBE_SelectionThenPinchInOOPIF) {
  // Load test URL with cross-process child.
  SetupTest();

  EXPECT_EQ(ui::TouchSelectionController::INACTIVE,
            root_rwhv()->touch_selection_controller()->active_status());
  // Find the location of some text to select.
  gfx::PointF point_f = GetPointInChild();

  // Initiate selection with a sequence of events that go through the targeting
  // system.
  selection_controller_client()->InitWaitForSelectionEvent(
      ui::SELECTION_HANDLES_SHOWN);

  SelectWithLongPress(gfx::Point(point_f.x(), point_f.y()));

  selection_controller_client()->Wait();

  // Check that selection is active and the quick menu is showing.
  EXPECT_EQ(ui::TouchSelectionController::SELECTION_ACTIVE,
            root_rwhv()->touch_selection_controller()->active_status());

  // Make sure handles are correctly positioned.
  VerifyHandlePosition();

  // Generate a pinch sequence, then re-verify handles are in the correct
  // location.
  float page_scale_delta = 2.f;
  float current_page_scale = PageScaleFactor();
  float target_page_scale = current_page_scale * page_scale_delta;

  SyntheticPinchGestureParams params;
  // We'll use the selection point for the pinch center to minimize the
  // likelihood of the selection getting zoomed offscreen.
  params.anchor = point_f;
  // Note: the |scale_factor| in |params| is actually treated as a delta, not
  // absolute, page scale.
  params.scale_factor = page_scale_delta;
  auto synthetic_pinch_gesture =
      std::make_unique<SyntheticTouchscreenPinchGesture>(params);

  auto* host =
      static_cast<RenderWidgetHostImpl*>(root_rwhv()->GetRenderWidgetHost());
  InputEventAckWaiter gesture_pinch_end_waiter(
      host, blink::WebInputEvent::Type::kGesturePinchEnd);
  host->QueueSyntheticGesture(
      std::move(synthetic_pinch_gesture),
      base::BindOnce(&TouchSelectionControllerClientAndroidSiteIsolationTest::
                         OnSyntheticGestureCompleted,
                     base::Unretained(this)));
  OnSyntheticGestureSent();
  // Make sure the gesture is complete from the renderer's point of view.
  gesture_pinch_end_waiter.Wait();

  VerifyHandlePosition();
  // TODO(wjmaclean): Investigate why SyntheticTouchscreenPinchGesture final
  // scales are so imprecise.
  // https://crbug.com/897173
  const float kScaleFactorTolerance = 0.05f;
  EXPECT_NEAR(target_page_scale, PageScaleFactor(), kScaleFactorTolerance);

  // Cleanup before shutting down.
  ShutdownTest();
}
#endif  // BUILDFLAG(IS_ANDROID)

class TouchEventObserver : public RenderWidgetHost::InputEventObserver {};

// This test verifies the ability of the TouchEventAckQueue to send TouchEvent
// acks to the root view in the correct order in the event of a slow renderer.
// This test uses a main-frame which acks instantly (no touch handler), and a
// child frame which acks very slowly. A synthetic gesture tap is sent to the
// child first, then the main frame. In this scenario, we expect the touch
// events sent to the main-frame to ack first, which will be problematic if
// the events are acked to the GestureRecognizer out of order.
//
// This test is disabled due to flakiness on all platforms, but especially on
// Android.  See https://crbug.com/945025.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       DISABLED_TouchEventAckQueueOrdering) {}

// Verify that sandbox flags specified by a CSP header are properly inherited by
// child frames, but are removed when the frame navigates.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       ActiveSandboxFlagsMaintainedAcrossNavigation) {}

// Test that after an RFH is unloaded, its old sandbox flags remain active.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       ActiveSandboxFlagsRetainedAfterUnload) {}

// Verify that when CSP-set sandbox flags on a page change due to navigation,
// the new flags are propagated to proxies in other SiteInstances.
//
//   A        A         A         A
//    \        \         \         \     .
//     B  ->    B*   ->   B*   ->   B*
//             /  \      /  \      /  \  .
//            B    B    A    B    C    B
//
// (B* has CSP-set sandbox flags)
// The test checks sandbox flags for the proxy added in step 2, by checking
// whether the grandchild frames navigated to in step 3 and 4 see the correct
// sandbox flags.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       ActiveSandboxFlagsCorrectInProxies) {}

// Verify that when the sandbox iframe attribute changes on a page which also
// has CSP-set sandbox flags, that the correct combination of flags is set in
// the sandboxed page after navigation.
//
//   A        A         A                                  A
//    \        \         \                                  \     .
//     B  ->    B*   ->   B*   -> (change sandbox attr) ->   B*
//             /  \      /  \                               /  \  .
//            B    B    A    B                             A'   B
//
// (B* has CSP-set sandbox flags)
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       ActiveSandboxFlagsCorrectAfterUpdate) {}

// Verify that when the sandbox iframe attribute is removed from a page which
// also has CSP-set sandbox flags, that the flags are cleared in the browser
// and renderers (including proxies) after navigation to a page without CSP-set
// flags.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       ActiveSandboxFlagsCorrectWhenCleared) {}

// Check that a subframe that requires a dedicated process will attempt to
// reuse an existing process for the same site, even across BrowsingInstances.
// This helps consolidate processes when running under --site-per-process.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       SubframeReusesExistingProcess) {}

// Check that when a subframe reuses an existing process for the same site
// across BrowsingInstances, a browser-initiated navigation in that subframe's
// tab doesn't unnecessarily share the reused process.  See
// https://crbug.com/803367.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       NoProcessSharingAfterSubframeReusesExistingProcess) {}

namespace {

// Intercepts the next DidCommitProvisionalLoad message for |deferred_url| in
// any frame of the |web_contents|, and holds off on dispatching it until
// *after* the DidCommitProvisionalLoad message for the next navigation in the
// |web_contents| has been dispatched.
//
// Reversing the order in which the commit messages are dispatched simulates a
// busy renderer that takes a very long time to actually commit the navigation
// to |deferred_url| after receiving FrameNavigationControl::CommitNavigation;
// whereas there is a fast cross-site navigation taking place in the same
// frame which starts second but finishes first.
class CommitMessageOrderReverser : public DidCommitNavigationInterceptor {};

}  // namespace

// Create an out-of-process iframe that causes itself to be detached during
// its layout/animate phase. See https://crbug.com/802932.
//
// TODO(crbug.com/40561636): Disabled on Android, Mac, and ChromeOS due to
// flakiness.
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS_ASH)
#define MAYBE_OOPIFDetachDuringAnimation
#else
#define MAYBE_OOPIFDetachDuringAnimation
#endif
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       MAYBE_OOPIFDetachDuringAnimation) {}

// Tests that a cross-process iframe asked to navigate to the same URL will
// successfully commit the navigation.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       IFrameSameDocumentNavigation) {}

// Verifies the the renderer has the size of the frame after commit.
// https://crbug/804046, https://crbug.com/801091
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, SizeAvailableAfterCommit) {}

// Test that a late mojo::AgentSchedulingGroupHost::DidUnloadRenderFrame won't
// incorrectly mark RenderViewHost as inactive if it's already been reused and
// switched to active by another navigation.  See https://crbug.com/823567.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       RenderViewHostStaysActiveWithLateUnloadACK) {}

// Check that when A opens a new window with B which embeds an A subframe, the
// subframe is visible and generates paint events.  See
// https://crbug.com/638375.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       SubframeVisibleAfterRenderViewBecomesSwappedOut) {}

IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, FrameDepthSimple) {}

IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, FrameDepthTest) {}

// Disabled due to flakiness. crbug.com/1146083
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
#define MAYBE_VisibilityFrameDepthTest
#else
#define MAYBE_VisibilityFrameDepthTest
#endif
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       MAYBE_VisibilityFrameDepthTest) {}

// Check that when a postMessage is called on a remote frame, it waits for the
// current script block to finish executing before forwarding the postMessage,
// so that if the script causes any other IPCs to be sent in the same event
// loop iteration, those IPCs are processed, and their side effects are
// observed by the target frame before it receives the forwarded postMessage.
// See https://crbug.com/828529.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       CrossProcessPostMessageWaitsForCurrentScriptToFinish) {}

// Ensure that if a cross-process postMessage is scheduled, and then the target
// frame is detached before the postMessage is forwarded, the source frame's
// renderer does not crash.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       CrossProcessPostMessageAndDetachTarget) {}

// Tests that the last committed URL is preserved on an RFH even after the RFH
// goes into the pending deletion state.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       LastCommittedURLRetainedAfterUnload) {}

#if BUILDFLAG(IS_ANDROID)

// This test ensures that gestures from child frames notify the gesture manager
// which exists only on the root frame. i.e. the gesture manager knows we're in
// a scroll gesture when it's happening in a cross-process child frame. This is
// important in cases like hiding the text selection popup during a scroll.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       GestureManagerListensToChildFrames) {
  GURL main_url(embedded_test_server()->GetURL(
      "a.com", "/cross_site_iframe_factory.html?a(b)"));
  EXPECT_TRUE(NavigateToURL(shell(), main_url));

  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
  FrameTreeNode* child = root->child_at(0);
  GURL b_url(embedded_test_server()->GetURL("b.com", "/scrollable_page.html"));
  EXPECT_TRUE(NavigateToURLFromRenderer(child, b_url));

  EXPECT_EQ(
      " Site A ------------ proxies for B\n"
      "   +--Site B ------- proxies for A\n"
      "Where A = http://a.com/\n"
      "      B = http://b.com/",
      DepictFrameTree(root));

  RenderWidgetHost* rwh = root->current_frame_host()->GetRenderWidgetHost();
  RenderWidgetHost* child_rwh =
      child->current_frame_host()->GetRenderWidgetHost();

  RunUntilInputProcessed(rwh);
  RunUntilInputProcessed(child_rwh);

  RenderWidgetHostViewAndroid* rwhv_root =
      static_cast<RenderWidgetHostViewAndroid*>(
          root->current_frame_host()->GetRenderWidgetHost()->GetView());

  ASSERT_FALSE(
      rwhv_root->gesture_listener_manager_->IsScrollInProgressForTesting());

  // Start a scroll gesture in the child frame, ensure the main frame's gesture
  // listener manager records that its in a scroll.
  {
    blink::WebGestureEvent gesture_scroll_begin(
        blink::WebGestureEvent::Type::kGestureScrollBegin,
        blink::WebInputEvent::kNoModifiers,
        blink::WebInputEvent::GetStaticTimeStampForTests(),
        blink::WebGestureDevice::kTouchscreen);
    gesture_scroll_begin.data.scroll_begin.delta_hint_units =
        ui::ScrollGranularity::kScrollByPrecisePixel;
    gesture_scroll_begin.data.scroll_begin.delta_x_hint = 0.f;
    // Note: Negative y-delta in a gesture event results in scrolling down on a
    // page (i.e. causes positive window.scrollY).
    gesture_scroll_begin.data.scroll_begin.delta_y_hint = -5.f;

    blink::WebMouseEvent mouse_move(
        blink::WebInputEvent::Type::kMouseMove,
        blink::WebInputEvent::kNoModifiers,
        blink::WebInputEvent::GetStaticTimeStampForTests());

    // We wait for the dummy mouse move event since the GestureScrollEnd ACK is
    // used change the gesture manager scrolling state but InputEventAckWaiter
    // is the first-in-line so the state won't yet be changed when it returns.
    // Thus we send a second event and when it's ACK'd we know the first has
    // already been processed (we do the same thing above but with a
    // ScrollUpdate).
    InputEventAckWaiter mouse_move_waiter(
        child_rwh, blink::WebInputEvent::Type::kMouseMove);

    child_rwh->ForwardGestureEvent(gesture_scroll_begin);
    child_rwh->ForwardMouseEvent(mouse_move);
    mouse_move_waiter.Wait();

    EXPECT_TRUE(
        rwhv_root->gesture_listener_manager_->IsScrollInProgressForTesting());
  }

  // Finish the scroll, ensure the gesture manager sees the scroll end.
  {
    blink::WebGestureEvent gesture_scroll_end(
        blink::WebGestureEvent::Type::kGestureScrollEnd,
        blink::WebInputEvent::kNoModifiers,
        blink::WebInputEvent::GetStaticTimeStampForTests(),
        blink::WebGestureDevice::kTouchscreen);

    // See comment above for why this is sent.
    blink::WebMouseEvent mouse_move(
        blink::WebInputEvent::Type::kMouseMove,
        blink::WebInputEvent::kNoModifiers,
        blink::WebInputEvent::GetStaticTimeStampForTests());

    InputEventAckWaiter mouse_move_waiter(
        child_rwh, blink::WebInputEvent::Type::kMouseMove);

    child_rwh->ForwardGestureEvent(gesture_scroll_end);
    child_rwh->ForwardMouseEvent(mouse_move);
    mouse_move_waiter.Wait();

    EXPECT_FALSE(
        rwhv_root->gesture_listener_manager_->IsScrollInProgressForTesting());
  }
}
#endif  // BUILDFLAG(IS_ANDROID)

IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, DisplayLockThrottlesOOPIF) {}

namespace {

// Helper class to intercept DidCommitProvisionalLoad messages and inject a
// call to close the current tab right before them.
class ClosePageBeforeCommitHelper : public DidCommitNavigationInterceptor {};

}  // namespace

// Verify that when a tab is closed just before a commit IPC arrives for a
// subframe in the tab, a subsequent resource timing IPC from the subframe RFH
// won't generate a renderer kill.  See https://crbug.com/805705.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       CloseTabBeforeSubframeCommits) {}

class SitePerProcessBrowserTouchActionTest : public SitePerProcessBrowserTest {};

#if BUILDFLAG(IS_ANDROID)
// Class to set |force_enable_zoom| to true in WebkitPrefs.
class EnableForceZoomContentClient
    : public ContentBrowserTestContentBrowserClient {
 public:
  EnableForceZoomContentClient() = default;

  EnableForceZoomContentClient(const EnableForceZoomContentClient&) = delete;
  EnableForceZoomContentClient& operator=(const EnableForceZoomContentClient&) =
      delete;

  void OverrideWebkitPrefs(WebContents* web_contents,
                           blink::web_pref::WebPreferences* prefs) override {
    prefs->force_enable_zoom = true;
  }
};

IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTouchActionTest,
                       ForceEnableZoomPropagatesToChild) {
  GURL main_url(embedded_test_server()->GetURL(
      "a.com", "/cross_site_iframe_factory.html?a(b)"));
  EXPECT_TRUE(NavigateToURL(shell(), main_url));
  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
  ASSERT_EQ(1U, root->child_count());
  GURL b_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
  FrameTreeNode* child = root->child_at(0);
  EXPECT_TRUE(NavigateToURLFromRenderer(child, b_url));
  WaitForHitTestData(child->current_frame_host());

  // Get access to child's TouchActionFilter.
  RenderWidgetHost* child_rwh =
      child->current_frame_host()->GetRenderWidgetHost();
  EXPECT_FALSE(GetTouchActionForceEnableZoom(child_rwh));

  EnableForceZoomContentClient new_client;

  web_contents()->OnWebPreferencesChanged();

  EXPECT_TRUE(GetTouchActionForceEnableZoom(child_rwh));

  // Add a new oopif child frame, and make sure it initializes with the correct
  // value of ForceEnableZoom.
  GURL c_url = embedded_test_server()->GetURL("c.com", "/title1.html");
  std::string create_frame_script = base::StringPrintf(
      "var new_iframe = document.createElement('iframe');"
      "new_iframe.src = '%s';"
      "document.body.appendChild(new_iframe);",
      c_url.spec().c_str());
  EXPECT_TRUE(ExecJs(root, create_frame_script));
  EXPECT_TRUE(WaitForLoadStop(web_contents()));
  ASSERT_EQ(2U, root->child_count());

  FrameTreeNode* new_child = root->child_at(1);
  EXPECT_NE(root->current_frame_host()->GetRenderWidgetHost(),
            new_child->current_frame_host()->GetRenderWidgetHost());
  EXPECT_TRUE(GetTouchActionForceEnableZoom(
      new_child->current_frame_host()->GetRenderWidgetHost()));
}

IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTouchActionTest,
                       CheckForceEnableZoomValue) {
  EXPECT_TRUE(NavigateToURL(
      shell(), embedded_test_server()->GetURL("foo.com", "/title1.html")));
  EXPECT_FALSE(GetTouchActionForceEnableZoom(
      web_contents()->GetPrimaryMainFrame()->GetRenderViewHost()->GetWidget()));

  EnableForceZoomContentClient new_client;

  web_contents()->OnWebPreferencesChanged();

  EXPECT_TRUE(GetTouchActionForceEnableZoom(
      web_contents()->GetPrimaryMainFrame()->GetRenderViewHost()->GetWidget()));

  EXPECT_TRUE(NavigateToURL(
      shell(), embedded_test_server()->GetURL("bar.com", "/title2.html")));

  EXPECT_TRUE(GetTouchActionForceEnableZoom(
      web_contents()->GetPrimaryMainFrame()->GetRenderViewHost()->GetWidget()));
}

#endif  // BUILDFLAG(IS_ANDROID)

// Flaky on every platform, failing most of the time on Android.
// See https://crbug.com/945734
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTouchActionTest,
                       DISABLED_EffectiveTouchActionPropagatesAcrossFrames) {}

// Flaky on all platform. http://crbug.com/9515270
IN_PROC_BROWSER_TEST_F(
    SitePerProcessBrowserTouchActionTest,
    DISABLED_EffectiveTouchActionPropagatesAcrossNestedFrames) {}

IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTouchActionTest,
                       EffectiveTouchActionPropagatesWhenChildFrameNavigates) {}

IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       ChildFrameCrashMetrics_KilledMainFrame) {}

IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       ChildFrameCrashMetrics_NeverShown) {}

IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       ChildFrameCrashMetrics_ScrolledIntoView) {}

class SitePerProcessAndProcessPerSiteBrowserTest
    : public SitePerProcessBrowserTest {};

// Verify that when --site-per-process is combined with --process-per-site, a
// cross-site, browser-initiated navigation with a generated page transition
// does not stay in the old SiteInstance.  See https://crbug.com/825411.
IN_PROC_BROWSER_TEST_P(SitePerProcessAndProcessPerSiteBrowserTest,
                       GeneratedTransitionsSwapProcesses) {}

namespace {

// Helper for waiting until next same-document navigation commits in
// |web_contents|.
class SameDocumentCommitObserver : public WebContentsObserver {};

}  // namespace

// Ensure that a same-document navigation does not cancel an ongoing
// cross-process navigation.  See https://crbug.com/825677.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       ReplaceStateDoesNotCancelCrossSiteNavigation) {}

// Test that a pending frame policy, such as an updated sandbox attribute, does
// not take effect after a same-document navigation.  See
// https://crbug.com/849311.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       SameDocumentNavigationDoesNotCommitPendingFramePolicy) {}

// Ensure that when two cross-site frames have subframes with unique origins,
// and those subframes create blob URLs and navigate to them, the blob URLs end
// up in different processes. See https://crbug.com/863623.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       TwoBlobURLsWithNullOriginDontShareProcess) {}

// Ensure that when a process is about to be destroyed after the last active
// frame in it goes away, an attempt to reuse a proxy in that process doesn't
// result in a crash.  See https://crbug.com/794625.
// TODO(crbug.com/42050611): This is flaky on Fuchsia because the
// MessagePort is not cleared on the other side, resulting in Zircon killing the
// process. See the comment referencing the same bug in
// //mojo/core/channel_fuchsia.cc
#if BUILDFLAG(IS_FUCHSIA)
#define MAYBE_RenderFrameProxyNotRecreatedDuringProcessShutdown
#else
#define MAYBE_RenderFrameProxyNotRecreatedDuringProcessShutdown
#endif
IN_PROC_BROWSER_TEST_P(
    SitePerProcessBrowserTest,
    MAYBE_RenderFrameProxyNotRecreatedDuringProcessShutdown) {}

IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       CommitTimeoutForHungRenderer) {}

// This is a regression test for https://crbug.com/881812 which complained that
// the hung renderer dialog used to undesirably show up for background tabs
// (typically during session restore when many navigations would be happening in
// backgrounded processes).
// TODO(crbug.com/40196588): Flaky on LaCrOS, Mac, and Windows.
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS_LACROS)
#define MAYBE_NoCommitTimeoutForInvisibleWebContents
#else
#define MAYBE_NoCommitTimeoutForInvisibleWebContents
#endif
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       MAYBE_NoCommitTimeoutForInvisibleWebContents) {}

// Tests that an inner WebContents will reattach to its outer WebContents after
// a navigation that causes a process swap.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, ProcessSwapOnInnerContents) {}

// This test ensures that WebContentsImpl::FocusOwningWebContents() focuses an
// inner WebContents when it is given an OOPIF's RenderWidgetHost inside that
// inner WebContents.  This setup isn't currently supported in Chrome
// (requiring issue 614463), but it can happen in embedders.  See
// https://crbug.com/1026056.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, FocusInnerContentsFromOOPIF) {}

// Check that a web frame can't navigate a remote subframe to a file: URL.  The
// frame should stay at the old URL, and the navigation attempt should produce
// a console error message.  See https://crbug.com/894399.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       FileURLBlockedWithConsoleErrorInRemoteFrameNavigation) {}

// Touchscreen DoubleTapZoom is only supported on Android & ChromeOS at present.
#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_ANDROID)
// A test ContentBrowserClient implementation which enforces
// WebPreferences' |double_tap_to_zoom_enabled| to be true.
class DoubleTapZoomContentBrowserClient
    : public ContentBrowserTestContentBrowserClient {
 public:
  DoubleTapZoomContentBrowserClient() = default;

  DoubleTapZoomContentBrowserClient(const DoubleTapZoomContentBrowserClient&) =
      delete;
  DoubleTapZoomContentBrowserClient& operator=(
      const DoubleTapZoomContentBrowserClient&) = delete;

  void OverrideWebkitPrefs(
      content::WebContents* web_contents,
      blink::web_pref::WebPreferences* web_prefs) override {
    web_prefs->double_tap_to_zoom_enabled = true;
  }
};

IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       TouchscreenAnimateDoubleTapZoomInOOPIF) {
  // Install a client forcing double-tap zoom to be enabled.
  DoubleTapZoomContentBrowserClient content_browser_client;
  web_contents()->OnWebPreferencesChanged();

  GURL main_url(embedded_test_server()->GetURL(
      "a.com", "/cross_site_iframe_factory.html?a(b)"));
  EXPECT_TRUE(NavigateToURL(shell(), main_url));

  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
  ASSERT_EQ(1u, root->child_count());
  FrameTreeNode* child_b = root->child_at(0);
  ASSERT_TRUE(child_b);

  RenderFrameSubmissionObserver observer_a(root);
  // We need to observe a root frame submission to pick up the initial page
  // scale factor.
  observer_a.WaitForAnyFrameSubmission();
  float original_page_scale =
      observer_a.LastRenderFrameMetadata().page_scale_factor;

  // Must do this before it's safe to use the coordinate transform functions.
  WaitForHitTestData(child_b->current_frame_host());

  // Select a tap point inside the OOPIF.
  gfx::PointF tap_position =
      child_b->current_frame_host()
          ->GetRenderWidgetHost()
          ->GetView()
          ->TransformPointToRootCoordSpaceF(gfx::PointF(10, 10));

  // Generate a double-tap.
  static constexpr char kActionsTemplate[] = R"HTML(
      [{
        "source" : "touch",
        "actions" : [
          { "name": "pointerDown", "x": %f, "y": %f},
          { "name": "pointerUp"},
          { "name": "pause", "duration": 50 },
          { "name": "pointerDown", "x": %f, "y": %f},
          { "name": "pointerUp"}
        ]
      }]
  )HTML";
  std::string double_tap_actions_json =
      base::StringPrintf(kActionsTemplate, tap_position.x(), tap_position.y(),
                         tap_position.x(), tap_position.y());
  auto parsed_json =
      base::JSONReader::ReadAndReturnValueWithError(double_tap_actions_json);
  ASSERT_TRUE(parsed_json.has_value()) << parsed_json.error().message;
  ActionsParser actions_parser(std::move(*parsed_json));

  ASSERT_TRUE(actions_parser.Parse());
  auto synthetic_gesture_doubletap = std::make_unique<SyntheticPointerAction>(
      actions_parser.pointer_action_params());

  // Queue the event and wait for it to be acked.
  InputEventAckWaiter ack_waiter(
      child_b->current_frame_host()->GetRenderWidgetHost(),
      blink::WebInputEvent::Type::kGestureDoubleTap);
  auto* host = static_cast<RenderWidgetHostImpl*>(
      root->current_frame_host()->GetRenderWidgetHost());
  host->QueueSyntheticGesture(
      std::move(synthetic_gesture_doubletap),
      base::BindOnce([](SyntheticGesture::Result result) {
        EXPECT_EQ(SyntheticGesture::GESTURE_FINISHED, result);
      }));
  // Waiting for the ack on the child frame ensures the event actually routed
  // through the oopif.
  ack_waiter.Wait();

  // Wait for page scale to change. We'll assume the OOPIF is scaled up by
  // at least 10%.
  float target_scale = 1.1f * original_page_scale;
  float new_page_scale = original_page_scale;
  do {
    observer_a.WaitForAnyFrameSubmission();
    new_page_scale = observer_a.LastRenderFrameMetadata().page_scale_factor;
  } while (new_page_scale < target_scale);
}
#endif  // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_ANDROID)

class CrossProcessNavigationObjectElementTest
    : public SitePerProcessBrowserTestBase,
      public testing::WithParamInterface<
          std::tuple<std::string, std::string, std::string>> {};

// This test verifies the correctness of rendering fallback in <object> when the
// a cross-origin navigation leads to a 404 error. Assuming the page's origin
// is "a.com", the test cases are:
// 1- Navigating an <object> from "a.com" to invalid "b.com" resource. In this
//    case the load fails for a provisional frame and at that time there is no
//    proxy to parent.
// 2- Navigating an <object> from "b.com" to invalid "b.com". Since navigation
//    is not cross-origin the failure happens for a non-provisional frame.
// 3- Navigation an <object> from "b.com" to invalid "c.com". The load fails for
//    a provisional frame, and at that time there is a proxy to parent.
IN_PROC_BROWSER_TEST_P(CrossProcessNavigationObjectElementTest, FallbackShown) {}

INSTANTIATE_TEST_SUITE_P();

#if !BUILDFLAG(IS_ANDROID)
// This test verifies that after occluding a WebContents the RAF inside a
// cross-process child frame is throttled.
// Disabled due to flakiness. crbug.com/1293207
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       DISABLED_OccludedRenderWidgetThrottlesRAF) {}
#endif

// Test that a renderer locked to origin A will be terminated if it tries to
// commit a navigation to origin B.  See also https://crbug.com/770239.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       CommittedOriginIncompatibleWithOriginLock) {}

// This test verifies that plugin elements containing cross-process-frames do
// not become unresponsive during style changes. (see https://crbug.com/781880).
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       PluginElementResponsiveInCrossProcessNavigations) {}

// Pending navigations must be canceled when a frame becomes pending deletion.
//
// 1) Initial state: A(B).
// 2) Navigation from B to C. The server is slow to respond.
// 3) Deletion of B.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       NavigationCommitInIframePendingDeletionAB) {}

// Pending navigations must be canceled when a frame becomes pending deletion.
//
// 1) Initial state: A(B(C)).
// 2) Navigation from C to D. The server is slow to respond.
// 3) Deletion of B.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       NavigationCommitInIframePendingDeletionABC) {}

// A same document commit from the renderer process is received while the
// RenderFrameHost is pending deletion.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       SameDocumentCommitWhilePendingDeletion) {}

// An history navigation from the renderer process is received while the
// RenderFrameHost is pending deletion.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       HistoryNavigationWhilePendingDeletion) {}

// One frame navigates using window.open while it is pending deletion. The two
// frames lives in different processes.
// See https://crbug.com/932087.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       OpenUrlToRemoteFramePendingDeletion) {}

// Check that if a frame starts a navigation, and the frame's current process
// dies before the response for the navigation comes back, the response will
// not trigger a process kill and will be allowed to commit in a new process.
// See https://crbug.com/968259.
// Note: This test needs to do a browser-initiated navigation because doing
// a renderer-initiated navigation would lead to the navigation being canceled.
// This behavior change has been introduced when navigation moved to use Mojo
// IPCs and is documented here https://crbug.com/988368.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       ProcessDiesBeforeCrossSiteNavigationCompletes) {}

enum class InnerWebContentsAttachChildFrameOriginType {};

class InnerWebContentsAttachTest
    : public SitePerProcessBrowserTestBase,
      public testing::WithParamInterface<
          std::tuple<InnerWebContentsAttachChildFrameOriginType,
                     bool /* original frame has beforeunload handlers */,
                     bool /* user proceeds with attaching */>> {};

// This is a test for the FrameTreeNode preparation process for various types
// of outer WebContents RenderFrameHosts; essentially when connecting two
// WebContents through a frame in a WebPage it is possible that the frame itself
// has a nontrivial document (other than about:blank) with a beforeunload
// handler, or even it is a cross-process frame. For such cases the frame first
// needs to be sanitized to be later consumed by the WebContents attaching API.
IN_PROC_BROWSER_TEST_P(InnerWebContentsAttachTest, PrepareFrame) {}

INSTANTIATE_TEST_SUITE_P();

// This checks what process is used when an iframe is navigated to about:blank.
// The new document should be loaded in the process of its initiator.
//
// Test case:
// 1. Navigate to A1(B2).
// 2. B2 navigates itself to B3 = about:blank. Process B is used.
// 3. A1 makes B3 to navigate to A4 = about:blank. Process A is used.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       SameAndCrossProcessIframeAboutBlankNavigation) {}

IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                       AccessWindowProxyOfCrashedFrameAfterNavigation) {}

// Make sure that a popup with a cross site subframe can be closed from the
// subframe.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, CloseNoopenerWindow) {}

// Check that initial navigations to renderer debug URLs mark the renderer
// process as used, so that future navigations to sites that require a
// dedicated process do not reuse that process.
IN_PROC_BROWSER_TEST_P(
    SitePerProcessBrowserTest,
    ProcessNotReusedAfterInitialNavigationToRendererDebugURL) {}

// Test that cross-site navigations clear user activation.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, UserActivationCrossSite) {}

// Test that same-site cross-origin navigations keep user activation.
// TODO(crbug.com/40228985): Find a way to reset activation here without
// breaking sites in practice.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, UserActivationSameSite) {}

// Test that same-origin navigations keep user activation.
// TODO(crbug.com/40228985): Find a way to reset activation here without
// breaking sites in practice.
IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, UserActivationSameOrigin) {}

// Tests that verify the feature disabling process reuse.
class DisableProcessReusePolicyTest : public SitePerProcessBrowserTest {};

// In two tabs with the same site, open a cross site iframe in each (same site
// for the iframes). Make sure these do not have the same process ID.
IN_PROC_BROWSER_TEST_P(DisableProcessReusePolicyTest,
                       DisableProcessReusePolicy) {}

class SitePerProcessWithMainFrameThresholdTestBase
    : public SitePerProcessBrowserTestBase {};

class SitePerProcessWithMainFrameThresholdTest
    : public SitePerProcessWithMainFrameThresholdTestBase,
      public ::testing::WithParamInterface<std::string> {};

// Tests that a RenderProcessHost is reused up to a certain threshold against
// number of main frames, if the corresponding SiteInstance requires a dedicated
// process. Subframes are irrelevant to the threshold. Once the number of main
// frame reaches to the threshold, a new RenderProcessHost should be created and
// the existing RenderProcessHost should not be reused.
IN_PROC_BROWSER_TEST_P(SitePerProcessWithMainFrameThresholdTest,
                       ReuseProcessUpToThreshold) {}

// A test fixture that provides an upper limit of 4 bytes, so should fail the
// assignment of another outermost main frame into the process.
class SitePerProcessWithMainFrameThresholdWithTotalLimitTest
    : public SitePerProcessWithMainFrameThresholdTestBase,
      public ::testing::WithParamInterface<std::string> {};

class RendererHostInterceptor
    : public mojom::RendererHostInterceptorForTesting {};

// Tests that a RenderProcessHost is not reused when the private memory
// footprint of the process exceeds a certain amount.
IN_PROC_BROWSER_TEST_P(SitePerProcessWithMainFrameThresholdWithTotalLimitTest,
                       ExcessiveAllocation) {}

// Tests that opening a fourth tab will put it over the limit and will allocate
// a new process. We allocate 3 main frames that are 2 bytes each. Placing
// a fourth would exceeded the limit of 9.
IN_PROC_BROWSER_TEST_P(SitePerProcessWithMainFrameThresholdWithTotalLimitTest,
                       AllowedAllocation) {}

// Tests that opening a new tab from an existing page via ctrl-click reuses a
// process when both pages are the same-site.
IN_PROC_BROWSER_TEST_P(SitePerProcessWithMainFrameThresholdTest,
                       ReuseProcessOpenTabByCtrlClickLink) {}

// Tests that opening a new tab from an existing page via window.open reuses a
// process when both pages are the same-site.
// TODO(crbug.com/40264958): Change this test to use 'noopener' once we
// figure out how to handle navigation from an empty site to a new site.
IN_PROC_BROWSER_TEST_P(SitePerProcessWithMainFrameThresholdTest,
                       ReuseProcessWithOpener) {}

class SitePerProcessWithMainFrameThresholdLocalhostTest
    : public SitePerProcessWithMainFrameThresholdTestBase,
      public ::testing::WithParamInterface<bool> {};

// Tests that process reuse is allowed or disallowed for localhost based on a
// feature parameter.
IN_PROC_BROWSER_TEST_P(SitePerProcessWithMainFrameThresholdLocalhostTest,
                       AllowReuseLocalHost) {}

class SitePerProcessWithMainFrameThresholdDevToolsTest
    : public SitePerProcessWithMainFrameThresholdTestBase,
      public TestDevToolsProtocolClient {};

// Tests that process reuse is diallowed when DevTools is attached to the
// renderer process.
IN_PROC_BROWSER_TEST_F(SitePerProcessWithMainFrameThresholdDevToolsTest,
                       DevToolsAttached) {}

INSTANTIATE_TEST_SUITE_P();
#if BUILDFLAG(IS_ANDROID)
INSTANTIATE_TEST_SUITE_P(All,
                         SitePerProcessAndroidImeTest,
                         testing::ValuesIn(RenderDocumentFeatureLevelValues()));
#endif  // BUILDFLAG(IS_ANDROID)
INSTANTIATE_TEST_SUITE_P();
INSTANTIATE_TEST_SUITE_P();
INSTANTIATE_TEST_SUITE_P();
INSTANTIATE_TEST_SUITE_P();
INSTANTIATE_TEST_SUITE_P();
INSTANTIATE_TEST_SUITE_P();
INSTANTIATE_TEST_SUITE_P();
INSTANTIATE_TEST_SUITE_P();
INSTANTIATE_TEST_SUITE_P();
#if BUILDFLAG(IS_ANDROID)
INSTANTIATE_TEST_SUITE_P(All,
                         TouchSelectionControllerClientAndroidSiteIsolationTest,
                         testing::ValuesIn(RenderDocumentFeatureLevelValues()));
#endif  // BUILDFLAG(IS_ANDROID)
INSTANTIATE_TEST_SUITE_P();

INSTANTIATE_TEST_SUITE_P();

}  // namespace content