chromium/chrome/browser/site_isolation/chrome_site_per_process_browsertest.cc

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

#include <utility>
#include <vector>

#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/chrome_content_browser_client.h"
#include "chrome/browser/external_protocol/external_protocol_handler.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/renderer_context_menu/render_view_context_menu_browsertest_util.h"
#include "chrome/browser/site_isolation/chrome_site_per_process_test.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/chrome_features.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/guest_view/browser/guest_view_base.h"
#include "components/guest_view/browser/guest_view_manager_delegate.h"
#include "components/guest_view/browser/test_guest_view_manager.h"
#include "components/javascript_dialogs/app_modal_dialog_controller.h"
#include "components/javascript_dialogs/app_modal_dialog_view.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/common/content_features.h"
#include "content/public/common/referrer.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/hit_test_region_observer.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/public/test/test_utils.h"
#include "extensions/browser/api/extensions_api_client.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "pdf/buildflags.h"
#include "third_party/blink/public/common/input/web_gesture_event.h"
#include "third_party/blink/public/common/input/web_input_event.h"
#include "ui/display/display_switches.h"
#include "ui/events/base_event_utils.h"
#include "ui/gfx/geometry/point.h"
#include "url/gurl.h"

#if BUILDFLAG(ENABLE_PDF)
#include "chrome/browser/pdf/test_pdf_viewer_stream_manager.h"
#include "pdf/pdf_features.h"
#endif

#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "ash/public/cpp/test/shell_test_api.h"
#include "ui/display/manager/display_manager.h"
#include "ui/display/screen.h"
#include "ui/display/test/display_manager_test_api.h"  // nogncheck
#endif

namespace {

class RedirectObserver : public content::WebContentsObserver {};

}  // namespace

class SitePerProcessHighDPIExpiredCertBrowserTest
    : public ChromeSitePerProcessTest {};

double GetFrameDeviceScaleFactor(const content::ToRenderFrameHost& adapter) {}

// Flaky on Windows 10. http://crbug.com/700150
#if BUILDFLAG(IS_WIN)
#define MAYBE_InterstitialLoadsWithCorrectDeviceScaleFactor
#else
#define MAYBE_InterstitialLoadsWithCorrectDeviceScaleFactor
#endif
IN_PROC_BROWSER_TEST_F(SitePerProcessHighDPIExpiredCertBrowserTest,
                       MAYBE_InterstitialLoadsWithCorrectDeviceScaleFactor) {}

// Verify that browser shutdown path works correctly when there's a
// RenderFrameProxyHost for a child frame.
IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTest, RenderFrameProxyHostShutdown) {}

// Verify that origin replication allows JS access to localStorage, database,
// and FileSystem APIs.  These features involve a check on the
// WebSecurityOrigin of the topmost WebFrame in ContentSettingsObserver, and
// this test ensures this check works when the top frame is remote.
IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTest,
                       OriginReplicationAllowsAccessToStorage) {}

// Ensure that creating a plugin in a cross-site subframe doesn't crash.  This
// involves querying content settings from the renderer process and using the
// top frame's origin as one of the parameters.  See https://crbug.com/426658.
IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTest, PluginWithRemoteTopFrame) {}

// Verify that ctrl-click of an anchor targeting a remote frame works (i.e. that
// it opens the link in a new tab).  See also https://crbug.com/647772.
IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTest,
                       AnchorCtrlClickWhenTargetIsCrossSite) {}

#if BUILDFLAG(ENABLE_PDF)
class ChromeSitePerProcessGuestViewPDFTest : public ChromeSitePerProcessTest {};

// This test verifies that when navigating an OOPIF to a page with <embed>-ed
// PDF, the guest is properly created, and by removing the embedder frame, the
// guest is properly destroyed (https://crbug.com/649856).
IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessGuestViewPDFTest,
                       EmbeddedPDFInsideCrossOriginFrame) {}

class ChromeSitePerProcessOopifPDFTest : public ChromeSitePerProcessTest {};

// This test verifies that when navigating an OOPIF to a page with <embed>-ed
// PDF, the PDF viewer stream manager is properly created, and by removing the
// embedder frame, the stream manager is properly destroyed.
IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessOopifPDFTest,
                       EmbeddedPDFInsideCrossOriginFrame) {}

// Check that navigating to a PDF and then trying to access localStorage or
// sessionStorage in the context of the PDF document fails gracefully and
// doesn't lead to a renderer kill. PDF documents don't access these interfaces
// directly, but the access could still happen via DevTools.  See
// https://crbug.com/357014503.
IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessOopifPDFTest,
                       AccessStorageInPDFDocument) {}
#endif  // BUILDFLAG(ENABLE_PDF)

// A helper class to verify that a "mailto:" external protocol request succeeds.
class MailtoExternalProtocolHandlerDelegate
    : public ExternalProtocolHandler::Delegate {};

// This test is not run on ChromeOS because it registers a custom handler (see
// ProtocolHandlerRegistry::InstallDefaultsForChromeOS), and handles mailto:
// navigations before getting to external protocol code.

// This test verifies that external protocol requests succeed when made from an
// OOPIF (https://crbug.com/668289).

// Disabled due to flakiness. If enabled, still skip for ChromeOS based on
// comment above.
// See https://crbug.com/980446
IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTest,
                       DISABLED_LaunchExternalProtocolFromSubframe) {}

// Verify that a popup can be opened after navigating a remote frame.  This has
// to be a chrome/ test to ensure that the popup blocker doesn't block the
// popup.  See https://crbug.com/670770.
IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTest,
                       NavigateRemoteFrameAndOpenPopup) {}

// Ensure that a transferred cross-process navigation does not generate
// DidStopLoading events until the navigation commits.  If it did, then
// ui_test_utils::NavigateToURL would proceed before the URL had committed.
// http://crbug.com/243957.
IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTest,
                       NoStopDuringTransferUntilCommit) {}

// Tests that a cross-process redirect will only cause the beforeunload
// handler to run once.
IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTest,
                       SingleBeforeUnloadAfterRedirect) {}

IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTest, PrintIgnoredInUnloadHandler) {}

// Ensure that when a window closes itself via window.close(), its process does
// not get destroyed if there's a pending cross-process navigation in the same
// process from another tab.  See https://crbug.com/799399.
IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTest,
                       ClosePopupWithPendingNavigationInOpener) {}

#if defined(USE_AURA)
// Test that with a desktop/laptop touchscreen, a two finger tap opens a
// context menu in an OOPIF.
IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTest, TwoFingerTapContextMenu) {}
#endif  // defined(USE_AURA)

// Check that cross-process postMessage preserves user gesture.  When a
// subframe with a user gesture postMessages its parent, the parent should be
// able to open a popup.  This test is in chrome/ so that it exercises the
// popup blocker logic.
IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTest,
                       CrossProcessPostMessagePreservesUserGesture) {}

// Check that when a frame sends two cross-process postMessages while having a
// user gesture, the recipient will only be able to create one popup.  This
// test is in chrome/ so that it exercises the popup blocker logic.
IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTest,
                       TwoPostMessagesWithSameUserGesture) {}

// Check that when a frame sends two postMessages to iframes on different sites
// while having a user gesture, the two recipient processes will only be able
// to create one popup. This test is in chrome/ so that it exercises the popup
// blocker logic.
IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTest,
                       TwoPostMessagesToDifferentSitesWithSameUserGesture) {}

// Check that when a frame sends a cross-process postMessage to a second frame
// while having a user gesture, and then the second frame sends another
// cross-process postMessage to a third frame, the second and third frames can
// only create one popup between the two of them. This test is in chrome/ so
// that it exercises the popup blocker logic.
IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTest,
                       PostMessageSendsSecondPostMessageWithUserGesture) {}

// Test that when a frame sends a cross-process postMessage and then requests a
// window.open(), and the message recipient also requests a window.open(), only
// one popup will be opened.
IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTest,
                       PostMessageSenderAndReceiverRaceToCreatePopup) {}

// Test that an activation is visible to the ancestors of the activated frame
// and not to the descendants.
IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTest,
                       UserActivationVisibilityInAncestorFrame) {}

// Test that when an activation is consumed, no frames in the frame tree can
// consume it again.
IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTest,
                       UserActivationConsumptionAcrossFrames) {}

// Test that opening a window with `noopener` consumes user activation.
// crbug.com/1264543, crbug.com/1291210
IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTest,
                       UserActivationConsumptionNoopener) {}

// TODO(crbug.com/40106376): Flaky.
// Tests that a cross-site iframe runs its beforeunload handler when closing a
// tab.  See https://crbug.com/853021.
IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTest,
                       DISABLED_TabCloseWithCrossSiteBeforeUnloadIframe) {}

// Tests that a same-site iframe runs its beforeunload handler when closing a
// tab.  Same as the test above, but for a same-site rather than cross-site
// iframe.  See https://crbug.com/1010456.
// Flaky (timeout) on Linux, ChromeOS, MacOS, and Windows (crbug.com/1033002)
IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTest,
                       DISABLED_TabCloseWithSameSiteBeforeUnloadIframe) {}

// Test that there's no crash in the following scenario:
// 1. a page opens a cross-site popup, where the popup has a cross-site iframe
//    with a beforeunload handler.
// 2. The user tries to close the popup, triggering beforeunload.
// 3. While waiting for the subframe's beforeunload, the original page closes
//    the popup via window.close().
// See https://crbug.com/866382.
IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTest,
                       CrossProcessWindowCloseWithBeforeUnloadIframe) {}

// Test that there's no crash in the following scenario:
// 1. a page opens a cross-site popup, where the popup has two cross-site
//    iframes, with second having a beforeunload handler.
// 2. The user tries to close the popup, triggering beforeunload.
// 3. While waiting for second subframe's beforeunload, the original page
//    closes the popup via window.close().
// See https://crbug.com/866382.  This is a variant of the test above, but
// with two iframes, which used to trigger a different crash.
IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTest,
                       CrossProcessWindowCloseWithTwoBeforeUnloadIframes) {}

class ChromeSitePerProcessTestWithVerifiedUserActivation
    : public ChromeSitePerProcessTest {};

// Test mouse down activation notification with browser verification.
// TODO(crbug.com/40826005): Flaky on Mac.
#if BUILDFLAG(IS_MAC)
#define MAYBE_UserActivationBrowserVerificationSameOriginSite
#else
#define MAYBE_UserActivationBrowserVerificationSameOriginSite
#endif
IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTestWithVerifiedUserActivation,
                       MAYBE_UserActivationBrowserVerificationSameOriginSite) {}

IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTest, JSPrintDuringSwap) {}

#if BUILDFLAG(IS_CHROMEOS_ASH)
// This test verifies that an OOPIF created in a tab on a secondary display
// doesn't initialize its device scale factor based on the primary display.
// Note: This test could probably be expanded to run on all ASH platforms.
// Disabled due to flakiness. https://crbug.com/1359423
IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTest,
                       DISABLED_TestInitialDSFForOOPIF) {
  // Spec for a two-display system, where the primary display has non-unit
  // device scale factor, but the secondary has unit device scale factor.
  // Note: this test could really work with any two scale factors, so long as
  // they're different.
  std::string display_spec("0+0-500x500*2,0+501-500x500");
  ash::ShellTestApi shell_test_api;
  display::test::DisplayManagerTestApi(shell_test_api.display_manager())
      .UpdateDisplay(display_spec);
  ASSERT_EQ(2u, shell_test_api.display_manager()->GetNumDisplays());
  display::test::DisplayManagerTestApi display_manager_test_api(
      shell_test_api.display_manager());

  display::Screen* screen = display::Screen::GetScreen();
  int64_t display2 = display_manager_test_api.GetSecondaryDisplay().id();
  screen->SetDisplayForNewWindows(display2);
  Browser* browser_on_secondary_display = CreateBrowser(browser()->profile());

  // Open a page with an OOPIF on the secondary display.
  GURL main_url(embedded_test_server()->GetURL(
      "a.com", "/cross_site_iframe_factory.html?a(b)"));
  content::ProxyDSFObserver observer;
  ASSERT_TRUE(
      ui_test_utils::NavigateToURL(browser_on_secondary_display, main_url));
  observer.WaitForOneProxyHostCreation();

  EXPECT_EQ(1u, observer.num_creations());
  // If the OOPIF correctly gets its device_scale_factor from the secondary
  // screen, then it will satisfy the following expectation.
  EXPECT_EQ(1.f, observer.get_proxy_host_dsf(0));
}
#endif