chromium/chrome/browser/site_isolation/site_per_process_interactive_browsertest.cc

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

#include <memory>

#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/run_until.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_timeouts.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/password_manager/chrome_password_manager_client.h"
#include "chrome/browser/renderer_context_menu/render_view_context_menu_browsertest_util.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/interactive_test_utils.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/security_state/core/security_state.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/focused_node_details.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_widget_host.h"
#include "content/public/browser/render_widget_host_iterator.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/fenced_frame_test_util.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 "extensions/common/constants.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/default_handlers.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "pdf/buildflags.h"
#include "ui/base/test/ui_controls.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/vector2d.h"
#include "url/gurl.h"

#if BUILDFLAG(ENABLE_PDF)
#include "base/test/with_feature_override.h"
#include "chrome/browser/pdf/test_pdf_viewer_stream_manager.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 "extensions/browser/guest_view/mime_handler_view/test_mime_handler_view_guest.h"
#include "pdf/pdf_features.h"
#include "third_party/abseil-cpp/absl/types/variant.h"
#endif  // BUILDFLAG(ENABLE_PDF)

namespace {

// Counts and returns the number of RenderWidgetHosts in the browser process.
size_t GetNumberOfRenderWidgetHosts() {}

// Waits and polls the current number of RenderWidgetHosts and stops when the
// number reaches |target_count|.
void WaitForRenderWidgetHostCount(size_t target_count) {}

}  // namespace

class SitePerProcessInteractiveBrowserTest : public InProcessBrowserTest {};

class SitePerProcessInteractiveFencedFrameBrowserTest
    : public SitePerProcessInteractiveBrowserTest,
      public testing::WithParamInterface<const char*> {};

// Check that document.hasFocus() works properly with out-of-process iframes.
// The test builds a page with four cross-site frames and then focuses them one
// by one, checking the value of document.hasFocus() in all frames.  For any
// given focused frame, document.hasFocus() should return true for that frame
// and all its ancestor frames.
IN_PROC_BROWSER_TEST_F(SitePerProcessInteractiveBrowserTest, DocumentHasFocus) {}

// Ensure that a cross-process subframe can receive keyboard events when in
// focus.
IN_PROC_BROWSER_TEST_F(SitePerProcessInteractiveBrowserTest,
                       SubframeKeyboardEventRouting) {}

// Ensure that sequential focus navigation (advancing focused elements with
// <tab> and <shift-tab>) works across cross-process subframes. This has 2 test
// cases that check sequential focus navigation for both <iframe> and
// <fencedframe> elements.
// The test sets up six inputs fields in a page with two cross-process
// subframes:
//                 child1            child2
//             /------------\    /------------\.
//             | 2. <input> |    | 4. <input> |
//  1. <input> | 3. <input> |    | 5. <input> |  6. <input>
//             \------------/    \------------/.
//
// The test then presses <tab> six times to cycle through focused elements 1-6.
// The test then repeats this with <shift-tab> to cycle in reverse order.
IN_PROC_BROWSER_TEST_P(SitePerProcessInteractiveFencedFrameBrowserTest,
                       SequentialFocusNavigation) {}

// Similar to the test above, but check that sequential focus navigation works
// with <object> tags that contain OOPIFs.
//
// The test sets up four inputs fields in a page with a <object> that contains
// an OOPIF:
//                <object>
//             /------------\.
//             | 2. <input> |
//  1. <input> | 3. <input> |  4. <input>
//             \------------/.
//
// The test then presses <tab> 4 times to cycle through focused elements 1-4.
// The test then repeats this with <shift-tab> to cycle in reverse order.
IN_PROC_BROWSER_TEST_F(SitePerProcessInteractiveBrowserTest,
                       SequentialFocusNavigationWithObject) {}

// Ensure that frames get focus when wrapping focus using <tab> or <shift-tab>.
// This has 2 test cases that check sequential focus navigation for both
// <iframe> and <fencedframe> elements.
// The test sets up two input fields in a page with one cross-process subframe:
//                  child
//              /------------\.
//  1. <input>  | 2. <input> |
//              \------------/.
//
// The test then presses <tab> to focus on elements 1, then <shift-tab> twice to
// focus on the omnibox followed by element 2. This tests that focus works as
// expected when wrapping through non-page UI elements. Specifically, this tests
// that fenced frames can properly get and verify focus if its RenderWidgetHost
// loses focus.
IN_PROC_BROWSER_TEST_P(SitePerProcessInteractiveFencedFrameBrowserTest,
                       SequentialFocusNavigationWrapAround) {}

// Ensure that frames can pass focus to subsequent frames if there is nothing
// focusable left in their frame. The test sets up two input fields in a page
// with three cross-process subframes:
//                  child1          child3
//              /------------\. /------------\.
//  1. <input>  |   child2   |  | 2. <input> |
//              | /--------\ |  \------------/.
//              | \--------/ |
//              \------------/.
//
// The test then presses <tab> twice to focus on elements 1 and 2.
// TODO(crbug.com/40276413): Re-enable this test once this bug is fixed.
IN_PROC_BROWSER_TEST_P(SitePerProcessInteractiveFencedFrameBrowserTest,
                       SequentialFocusNavigationPassThrough) {}

// Ensure that frames can pass focus to subsequent frames if there is nothing
// focusable left in their frame. The test sets up two input fields in the last
// subframe loaded into a page:
//       child1          child3
//   /------------\. /-------------\.
//   |   child2   |  | 1. <input1> |
//   | /--------\ |  \ 2. <input2> /
//   | \--------/ |  \-------------/.
//   \------------/.
//
// The test then presses <tab> twice to focus on elements 1 and 2, <tab> to
// move focus to the UI, and <tab> one more time to focus on element 1 again.
// TODO(crbug.com/40276413): Re-enable this test once this bug is fixed.
IN_PROC_BROWSER_TEST_P(SitePerProcessInteractiveFencedFrameBrowserTest,
                       SequentialFocusWrapBackIntoChildFrame) {}

// TODO(crbug.com/40118868): Revisit the macro expression once build flag switch
// of lacros-chrome is complete.
#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) || BUILDFLAG(IS_WIN)
// Ensures that renderers know to advance focus to sibling frames and parent
// frames in the presence of mouse click initiated focus changes.
// Verifies against regression of https://crbug.com/702330
IN_PROC_BROWSER_TEST_F(SitePerProcessInteractiveBrowserTest,
                       TabAndMouseFocusNavigation) {}
#endif

namespace {

// Helper to retrieve the frame's (window.innerWidth, window.innerHeight).
gfx::Size GetFrameSize(content::RenderFrameHost* frame) {}

// Helper to check |frame|'s document.webkitFullscreenElement and return its ID
// if it's defined (which is the case when |frame| is in fullscreen mode), or
// "none" otherwise.
std::string GetFullscreenElementId(content::RenderFrameHost* frame) {}

// Helper to check if an element with ID |element_id| has the
// :-webkit-full-screen style.
bool ElementHasFullscreenStyle(content::RenderFrameHost* frame,
                               const std::string& element_id) {}

// Helper to check if an element with ID |element_id| has the
// :-webkit-full-screen-ancestor style.
bool ElementHasFullscreenAncestorStyle(content::RenderFrameHost* host,
                                       const std::string& element_id) {}

// Add a listener that will send back a message whenever the (prefixed)
// fullscreenchange event fires.  The message will be "fullscreenchange",
// followed by a space and the provided |id|.
void AddFullscreenChangeListener(content::RenderFrameHost* frame,
                                 const std::string& id) {}

// Helper to add a listener that will send back a "resize" message when the
// target |frame| is resized to |expected_size|.
void AddResizeListener(content::RenderFrameHost* frame,
                       const gfx::Size& expected_size) {}

// Helper to wait for a toggle fullscreen operation to complete in all affected
// frames.  This means waiting for:
// 1. All fullscreenchange events with id's matching the list in
//    |expected_fullscreen_event_ids|.  Typically the list will correspond to
//    events from the actual fullscreen element and all of its ancestor
//    <iframe> elements.
// 2. A resize event.  This will verify that the frame containing the
//    fullscreen element is properly resized.  This assumes that the expected
//    size is already registered via AddResizeListener().
void WaitForMultipleFullscreenEvents(
    const std::set<std::string>& expected_fullscreen_event_ids,
    content::DOMMessageQueue& queue) {}

}  // namespace

// Check that an element in a cross-process subframe can enter and exit
// fullscreen.  The test will verify that:
// - the subframe is properly resized
// - the WebContents properly enters/exits fullscreen.
// - document.webkitFullscreenElement is correctly updated in both frames.
// - fullscreenchange events fire in both frames.
// - fullscreen CSS is applied correctly in both frames.
//
#if BUILDFLAG(IS_MAC)
// https://crbug.com/850594
#define MAYBE_FullscreenElementInSubframe
#else
#define MAYBE_FullscreenElementInSubframe
#endif
IN_PROC_BROWSER_TEST_F(SitePerProcessInteractiveBrowserTest,
                       MAYBE_FullscreenElementInSubframe) {}

// Check that on a page with A-embed-B-embed-A frame hierarchy, an element in
// the bottom frame can enter and exit fullscreen.  |exit_method| specifies
// whether to use browser-initiated vs. renderer-initiated fullscreen exit
// (i.e., pressing escape vs. a JS call), since they trigger different code
// paths on the Blink side.
void SitePerProcessInteractiveBrowserTest::FullscreenElementInABA(
    FullscreenExitMethod exit_method) {}

// https://crbug.com/1087392: Flaky for ASAN and TSAN
#if BUILDFLAG(IS_MAC) || defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER)
#define MAYBE_FullscreenElementInABAAndExitViaEscapeKey
#else
#define MAYBE_FullscreenElementInABAAndExitViaEscapeKey
#endif
IN_PROC_BROWSER_TEST_F(SitePerProcessInteractiveBrowserTest,
                       MAYBE_FullscreenElementInABAAndExitViaEscapeKey) {}

// This test is flaky on Linux (crbug.com/851236) and also not working
// on Mac (crbug.com/850594).
IN_PROC_BROWSER_TEST_F(SitePerProcessInteractiveBrowserTest,
                       DISABLED_FullscreenElementInABAAndExitViaJS) {}

// Check that fullscreen works on a more complex page hierarchy with multiple
// local and remote ancestors.  The test uses this frame tree:
//
//             A (a_top)
//             |
//             A (a_bottom)
//            / \   .
// (b_first) B   B (b_second)
//               |
//               C (c_top)
//               |
//               C (c_middle) <- fullscreen target
//               |
//               C (c_bottom)
//
// The c_middle frame will trigger fullscreen for its <div> element.  The test
// verifies that its ancestor chain is properly updated for fullscreen, and
// that the b_first node that's not on the chain is not affected.
//
// The test also exits fullscreen by simulating pressing ESC rather than using
// document.webkitExitFullscreen(), which tests the browser-initiated
// fullscreen exit path.
// TODO(crbug.com/40535621): flaky on all platforms.
IN_PROC_BROWSER_TEST_F(SitePerProcessInteractiveBrowserTest,
                       DISABLED_FullscreenElementInMultipleSubframes) {}

// Test that deleting a RenderWidgetHost that holds the mouse lock won't cause a
// crash. https://crbug.com/619571.

// Flaky on multiple builders. https://crbug.com/1059632
IN_PROC_BROWSER_TEST_F(SitePerProcessInteractiveBrowserTest,
                       DISABLED_RenderWidgetHostDeletedWhileMouseLocked) {}

#if BUILDFLAG(ENABLE_PDF)
// Base test class for interactive tests which load and test PDF files.
class SitePerProcessInteractivePDFTest
    : public base::test::WithFeatureOverride,
      public SitePerProcessInteractiveBrowserTest {};

// This test loads a PDF inside an OOPIF and then verifies that context menu
// shows up at the correct position.
// TODO(crbug.com/1423184, crbug.com/327338993): Fix flaky test.
#if BUILDFLAG(IS_WIN) || (BUILDFLAG(IS_LINUX) && defined(ADDRESS_SANITIZER))
#define MAYBE_ContextMenuPositionForEmbeddedPDFInCrossOriginFrame
#else
#define MAYBE_ContextMenuPositionForEmbeddedPDFInCrossOriginFrame
#endif  // BUILDFLAG(IS_WIN) || (BUILDFLAG(IS_LINUX) &&
        // defined(ADDRESS_SANITIZER))
IN_PROC_BROWSER_TEST_P(
    SitePerProcessInteractivePDFTest,
    MAYBE_ContextMenuPositionForEmbeddedPDFInCrossOriginFrame) {}

IN_PROC_BROWSER_TEST_P(SitePerProcessInteractivePDFTest,
                       LoadingPdfDoesNotStealFocus) {}

// TODO(crbug.com/40268279): Stop testing both modes after OOPIF PDF viewer
// launches.
INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE();
#endif  // BUILDFLAG(ENABLE_PDF)

class SitePerProcessAutofillTest : public SitePerProcessInteractiveBrowserTest {};

// Waits until transforming |sample_point| from |render_frame_host| coordinates
// to its root frame's view's coordinates matches |transformed_point| within a
// reasonable error margin less than or equal to |bound|. This method is used to
// verify CSS changes on OOPIFs have been applied properly and the corresponding
// compositor frame is updated as well. This way we can rest assured that the
// future transformed and reported bounds for the elements inside
// |render_frame_host| are correct.
void WaitForFramePositionUpdated(content::RenderFrameHost* render_frame_host,
                                 const gfx::Point& sample_point,
                                 const gfx::Point& transformed_point,
                                 float bound) {}

// This test verifies that when clicking outside the bounds of a date picker
// associated with an <input> inside an OOPIF, the RenderWidgetHostImpl
// corresponding to the WebPagePopup is destroyed (see
// https://crbug.com/671732).
IN_PROC_BROWSER_TEST_F(SitePerProcessInteractiveBrowserTest,
                       ShowAndHideDatePopupInOOPIFMultipleTimes) {}

// There is a problem of missing keyup events with the command key after
// the NSEvent is sent to NSApplication in ui/base/test/ui_controls_mac.mm .
// This test is disabled on only the Mac until the problem is resolved.
// See http://crbug.com/425859 for more information.
#if BUILDFLAG(IS_MAC)
#define MAYBE_SubframeAnchorOpenedInBackgroundTab
#else
#define MAYBE_SubframeAnchorOpenedInBackgroundTab
#endif
// Tests that ctrl-click in a subframe results in a background, not a foreground
// tab - see https://crbug.com/804838.  This test is somewhat similar to
// CtrlClickShouldEndUpIn*ProcessTest tests, but this test has to simulate an
// actual mouse click.
IN_PROC_BROWSER_TEST_F(SitePerProcessInteractiveBrowserTest,
                       MAYBE_SubframeAnchorOpenedInBackgroundTab) {}

// Check that window.focus works for cross-process popups.
// Flaky on ChromeOS debug and ASAN builds. https://crbug.com/1326293
// Flaky on Linux https://crbug.com/1336109.
// Flaky on Win https://crbug.com/1337725.
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN) || \
    (BUILDFLAG(IS_CHROMEOS) &&                  \
     (!defined(NDEBUG) || defined(ADDRESS_SANITIZER)))
#define MAYBE_PopupWindowFocus
#else
#define MAYBE_PopupWindowFocus
#endif
IN_PROC_BROWSER_TEST_F(SitePerProcessInteractiveBrowserTest,
                       MAYBE_PopupWindowFocus) {}

INSTANTIATE_TEST_SUITE_P();