// 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. #ifndef CONTENT_PUBLIC_TEST_BROWSER_TEST_UTILS_H_ #define CONTENT_PUBLIC_TEST_BROWSER_TEST_UTILS_H_ #include <memory> #include <optional> #include <string> #include <string_view> #include <utility> #include <vector> #include "base/containers/flat_set.h" #include "base/containers/queue.h" #include "base/files/scoped_temp_dir.h" #include "base/functional/callback.h" #include "base/functional/callback_forward.h" #include "base/json/json_writer.h" #include "base/memory/raw_ptr.h" #include "base/memory/raw_ptr_exclusion.h" #include "base/memory/weak_ptr.h" #include "base/process/process.h" #include "base/run_loop.h" #include "base/scoped_observation.h" #include "base/test/metrics/histogram_tester.h" #include "base/types/strong_alias.h" #include "base/types/to_address.h" #include "build/build_config.h" #include "cc/test/pixel_test_utils.h" #include "content/public/browser/commit_deferring_condition.h" #include "content/public/browser/devtools_agent_host.h" #include "content/public/browser/global_routing_id.h" #include "content/public/browser/render_frame_metadata_provider.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_process_host_observer.h" #include "content/public/browser/render_widget_host.h" #include "content/public/browser/web_contents_delegate.h" #include "content/public/browser/web_contents_media_capture_id.h" #include "content/public/browser/web_contents_observer.h" #include "content/public/common/isolated_world_ids.h" #include "content/public/common/page_type.h" #include "content/public/test/test_utils.h" #include "ipc/message_filter.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/receiver_set.h" #include "mojo/public/cpp/bindings/self_owned_associated_receiver.h" #include "mojo/public/cpp/test_support/test_utils.h" #include "net/base/load_flags.h" #include "net/cookies/cookie_options.h" #include "net/cookies/cookie_partition_key_collection.h" #include "services/metrics/public/cpp/ukm_source_id.h" #include "services/network/public/mojom/cookie_manager.mojom-forward.h" #include "storage/common/file_system/file_system_types.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/common/blob/blob_utils.h" #include "third_party/blink/public/common/context_menu_data/untrustworthy_context_menu_params.h" #include "third_party/blink/public/common/input/web_input_event.h" #include "third_party/blink/public/common/input/web_mouse_event.h" #include "third_party/blink/public/common/input/web_mouse_wheel_event.h" #include "third_party/blink/public/mojom/blob/blob_url_store.mojom-test-utils.h" #include "third_party/blink/public/mojom/blob/blob_url_store.mojom.h" #include "third_party/blink/public/mojom/frame/user_activation_update_types.mojom.h" #include "third_party/blink/public/mojom/keyboard_lock/keyboard_lock.mojom-shared.h" #include "third_party/blink/public/mojom/navigation/navigation_params.mojom-shared.h" #include "third_party/perfetto/include/perfetto/tracing/traced_value_forward.h" #include "ui/accessibility/ax_node_data.h" #include "ui/accessibility/ax_tree_update.h" #include "ui/display/display_switches.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/events/types/event_type.h" #include "url/gurl.h" #if BUILDFLAG(IS_WIN) #include "base/win/scoped_handle.h" #endif namespace gfx { class Point; } // namespace gfx namespace net { class CanonicalCookie; namespace test_server { class EmbeddedTestServer; } // namespace test_server // TODO(svaldez): Remove typedef once EmbeddedTestServer has been migrated // out of net::test_server. EmbeddedTestServer; } // namespace net namespace ui { class AXPlatformNodeDelegate; class AXTreeID; } // namespace ui #if BUILDFLAG(IS_WIN) namespace Microsoft { namespace WRL { template <typename> class ComPtr; } // namespace WRL } // namespace Microsoft typedef int PROPERTYID; #endif // A collections of functions designed for use with content_browsertests and // browser_tests. // TO BE CLEAR: any function here must work against both binaries. If it only // works with browser_tests, it should be in chrome\test\base\ui_test_utils.h. // If it only works with content_browsertests, it should be in // content\test\content_browser_test_utils.h. namespace blink { class StorageKey; struct TransferableMessage; namespace mojom { class FrameWidget; } // namespace mojom } // namespace blink namespace storage { class BlobUrlRegistry; } namespace content { #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) class MockCapturedSurfaceController; #endif // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) #if defined(USE_AURA) class SelectionBoundsWaiter; #endif // defined(USE_AURA) class BrowserContext; class FileSystemAccessPermissionContext; class FrameTreeNode; class NavigationHandle; class NavigationRequest; class RenderFrameMetadataProviderImpl; class RenderFrameProxyHost; class RenderWidgetHost; class RenderWidgetHostImpl; class RenderWidgetHostView; class ScopedAllowRendererCrashes; class ToRenderFrameHost; class WebContents; // This encapsulates the pattern of waiting for an event and returning whether // that event was received from `Wait`. This makes it easy to do the right thing // in Wait, i.e. return with `[[nodiscard]]`. class WaiterHelper { … }; // Navigates |web_contents| to |url|, blocking until the navigation finishes. // Returns true if the page was loaded successfully and the last committed URL // matches |url|. This is a browser-initiated navigation that simulates a user // typing |url| into the address bar. [[nodiscard]] bool NavigateToURL(WebContents* web_contents, const GURL& url); // Same as above, but takes in an additional URL, |expected_commit_url|, to // which the navigation should eventually commit. This is useful for cases // like redirects, where navigation starts on one URL but ends up committing a // different URL. This function will return true if navigating to |url| // results in a successful commit to |expected_commit_url|. [[nodiscard]] bool NavigateToURL(WebContents* web_contents, const GURL& url, const GURL& expected_commit_url); // Navigates |web_contents| to |url|, blocking until the given number of // navigations finishes. If |ignore_uncommitted_navigations| is true, then an // aborted navigation also counts toward |number_of_navigations| being complete. void NavigateToURLBlockUntilNavigationsComplete( WebContents* web_contents, const GURL& url, int number_of_navigations, bool ignore_uncommitted_navigations = true); // Same, but allows specifying the full LoadURLParams instead of just the URL. void NavigateToURLBlockUntilNavigationsComplete( WebContents* web_contents, const NavigationController::LoadURLParams& params, int number_of_navigations, bool ignore_uncommitted_navigations = true); // Helpers for performing renderer-initiated navigations. Note these helpers are // only suitable when the navigation is expected to at least start successfully, // i.e. result in a `DidStartNavigation()` notification. // // To write a test where the renderer itself blocks or cancels the navigation, // use `ExecJs()` or `EvalJs()`, e.g.: // // EXPECT_THAT(ExecJs("try { location = ''; } catch (e) { e.message; }") // .ExtractString(), // HasSubStr("...")); // // To write a test where the navigation results in a bad message kill, use // `ExecuteScriptAsync()` + `RenderProcessHostBadMojoMessageWaiter`, e.g.: // // ExecuteScriptAsync(rfh, JsReplace("location = $1", "/title2.html")); // RenderProcessHostBadMojoMessageWaiter kill_waiter(rfh->GetProcess()); // EXPECT_THAT(kill_waiter.Wait(), Optional(HasSubstr("..."))); // Perform a renderer-initiated navigation of the frame |adapter| to |url|, // blocking until the navigation finishes. The navigation is done by assigning // location.href in the frame |adapter|. Returns true if the page was loaded // successfully and the last committed URL matches |url|. [[nodiscard]] bool NavigateToURLFromRenderer(const ToRenderFrameHost& adapter, const GURL& url); // Similar to above but takes in an additional URL, |expected_commit_url|, to // which the navigation should eventually commit. (See the browser-initiated // counterpart for more details). [[nodiscard]] bool NavigateToURLFromRenderer(const ToRenderFrameHost& adapter, const GURL& url, const GURL& expected_commit_url); // Similar to the two helpers above, but perform the renderer-initiated // navigation without a user gesture. [[nodiscard]] bool NavigateToURLFromRendererWithoutUserGesture( const ToRenderFrameHost& adapter, const GURL& url); [[nodiscard]] bool NavigateToURLFromRendererWithoutUserGesture( const ToRenderFrameHost& adapter, const GURL& url, const GURL& expected_commit_url); // Perform a renderer-initiated navigation of the frame `adapter` to `url`. // Returns true if the navigation started successfully and false otherwise. [[nodiscard]] bool BeginNavigateToURLFromRenderer( const ToRenderFrameHost& adapter, const GURL& url); // Navigate a frame with ID |iframe_id| to |url|, blocking until the navigation // finishes. Uses a renderer-initiated navigation from script code in the // main frame. // // This method does not trigger a user activation before the navigation. If // necessary, a user activation can be triggered right before calling this // method, e.g. by calling |ExecJs(frame_tree_node, "")|. bool NavigateIframeToURL(WebContents* web_contents, std::string_view iframe_id, const GURL& url); // Similar to |NavigateIframeToURL()| but returns as soon as the navigation is // initiated. bool BeginNavigateIframeToURL(WebContents* web_contents, std::string_view iframe_id, const GURL& url); // Generate a URL for a file path including a query string. GURL GetFileUrlWithQuery(const base::FilePath& path, std::string_view query_string); // Checks whether the page type of the last committed navigation entry matches // |page_type|. bool IsLastCommittedEntryOfPageType(WebContents* web_contents, content::PageType page_type); // Waits for |web_contents| to stop loading. If |web_contents| is not loading // returns immediately. Tests should use WaitForLoadStop instead and check that // last navigation succeeds, and this function should only be used if the // navigation leads to web_contents being destroyed. void WaitForLoadStopWithoutSuccessCheck(WebContents* web_contents); // Waits for |web_contents| to stop loading. If |web_contents| is not loading // returns immediately. Returns true if the last navigation succeeded (resulted // in a committed navigation entry of type PAGE_TYPE_NORMAL). // TODO(alexmos): tests that use this function to wait for successful // navigations should be refactored to do EXPECT_TRUE(WaitForLoadStop()). bool WaitForLoadStop(WebContents* web_contents); // If a test uses a beforeunload dialog, it must be prepared to avoid flakes. // This function collects everything that needs to be done, except for user // activation which is triggered only when |trigger_user_activation| is true. // Note that beforeunload dialog attempts are ignored unless the frame has // received a user activation. void PrepContentsForBeforeUnloadTest(WebContents* web_contents, bool trigger_user_activation = true); #if defined(USE_AURA) || BUILDFLAG(IS_ANDROID) // If WebContent's view is currently being resized, this will wait for the ack // from the renderer that the resize is complete and for the // WindowEventDispatcher to release the pointer moves. If there's no resize in // progress, the method will return right away. void WaitForResizeComplete(WebContents* web_contents); #endif // defined(USE_AURA) || BUILDFLAG(IS_ANDROID) void NotifyCopyableViewInWebContents(WebContents* web_contents, base::OnceClosure done_callback); void NotifyCopyableViewInFrame(RenderFrameHost* render_frame_host, base::OnceClosure done_callback); // Allows tests to set the last committed origin of |render_frame_host|, to // simulate a scenario that might happen with a compromised renderer or might // not otherwise be possible. void OverrideLastCommittedOrigin(RenderFrameHost* render_frame_host, const url::Origin& origin); // Causes the specified web_contents to crash. Blocks until it is crashed. void CrashTab(WebContents* web_contents); // Sets up a commit interceptor to alter commits for |target_url| to change // their commit URL to |new_url| and origin to |new_origin|. This will happen // for all commits in |web_contents|. void PwnCommitIPC(WebContents* web_contents, const GURL& target_url, const GURL& new_url, const url::Origin& new_origin); // Test helper to check the content-internal CanCommitURL() method on // ChildProcessSecurityPolicy, determining whether a particular process is // allowed to commit a navigation to `url`. bool CanCommitURLForTesting(int child_id, const GURL& url); // Causes the specified web_contents to issue an OnUnresponsiveRenderer event // to its observers. void SimulateUnresponsiveRenderer(WebContents* web_contents, RenderWidgetHost* widget); // Simulates clicking at the center of the given tab asynchronously; modifiers // may contain bits from WebInputEvent::Modifiers. Sends the event through // RenderWidgetHostInputEventRouter and thus can target OOPIFs. If an OOPIF is // the intended target, ensure that its hit test data is available for routing, // using `WaitForHitTestData`, first. // Note: For simulating clicks inside a fenced frame tree, this function does // not work. Use `SimulateClickInFencedFrameTree` in // `content/public/test/fenced_frame_test_util.cc` void SimulateMouseClick(WebContents* web_contents, int modifiers, blink::WebMouseEvent::Button button); // Simulates clicking at the point |point| of the given tab asynchronously; // modifiers may contain bits from WebInputEvent::Modifiers. Sends the event // through RenderWidgetHostInputEventRouter and thus can target OOPIFs. If an // OOPIF is the intended target, ensure that its hit test data is available for // routing, using `WaitForHitTestData`, first. // Note: For simulating clicks inside a fenced frame tree, this function does // not work. Use `SimulateClickInFencedFrameTree` in // `content/public/test/fenced_frame_test_util.cc` void SimulateMouseClickAt(WebContents* web_contents, int modifiers, blink::WebMouseEvent::Button button, const gfx::Point& point); // Retrieves the center coordinates of the element with id |id|. // ATTENTION: When using these coordinates to simulate a click or tap make sure // that the viewport is not zoomed as the coordinates returned by this method // are relative to the page not the viewport. In particular for Android make // sure the page has the meta tag // <meta name="viewport" content="width=device-width,minimum-scale=1"> // TODO(crbug.com/40177926): Make the Simulate* methods more user // friendly by taking zooming into account. gfx::PointF GetCenterCoordinatesOfElementWithId( const ToRenderFrameHost& adapter, std::string_view id); // Retrieves the center coordinates of the element with id |id| and simulates a // mouse click there using SimulateMouseClickAt(). void SimulateMouseClickOrTapElementWithId(content::WebContents* web_contents, std::string_view id); // Simulates asynchronously a mouse enter/move/leave event. The mouse event is // routed through RenderWidgetHostInputEventRouter and thus can target OOPIFs. void SimulateMouseEvent(WebContents* web_contents, blink::WebInputEvent::Type type, const gfx::Point& point); void SimulateMouseEvent(WebContents* web_contents, blink::WebInputEvent::Type type, blink::WebMouseEvent::Button button, const gfx::Point& point); // Simulate a mouse wheel event. void SimulateMouseWheelEvent(WebContents* web_contents, const gfx::Point& point, const gfx::Vector2d& delta, const blink::WebMouseWheelEvent::Phase phase); #if !BUILDFLAG(IS_MAC) // Simulate a mouse wheel event with the ctrl modifier set. void SimulateMouseWheelCtrlZoomEvent(RenderWidgetHost* render_widget_host, const gfx::Point& point, bool zoom_in, blink::WebMouseWheelEvent::Phase phase); void SimulateTouchscreenPinch(WebContents* web_contents, const gfx::PointF& anchor, float scale_change, base::OnceClosure on_complete); #endif // !BUILDFLAG(IS_MAC) // Sends a GesturePinch Begin/Update/End sequence. void SimulateGesturePinchSequence(RenderWidgetHost* render_widget_host, const gfx::Point& point, float scale, blink::WebGestureDevice source_device); void SimulateGesturePinchSequence(WebContents* web_contents, const gfx::Point& point, float scale, blink::WebGestureDevice source_device); // Sends a simple, three-event (Begin/Update/End) gesture scroll. void SimulateGestureScrollSequence(RenderWidgetHost* render_widget_host, const gfx::Point& point, const gfx::Vector2dF& delta); void SimulateGestureScrollSequence(WebContents* web_contents, const gfx::Point& point, const gfx::Vector2dF& delta); void SimulateGestureEvent(RenderWidgetHost* render_widget_host, const blink::WebGestureEvent& gesture_event, const ui::LatencyInfo& latency); void SimulateGestureEvent(WebContents* web_contents, const blink::WebGestureEvent& gesture_event, const ui::LatencyInfo& latency); // Taps the screen at |point|, using gesture Tap or TapDown. void SimulateTapAt(WebContents* web_contents, const gfx::Point& point); void SimulateTapDownAt(WebContents* web_contents, const gfx::Point& point); // A helper function for SimulateTap(Down)At. void SimulateTouchGestureAt(WebContents* web_contents, const gfx::Point& point, blink::WebInputEvent::Type type); #if defined(USE_AURA) // Generates a TouchEvent of |event_type| at |point|. void SimulateTouchEventAt(WebContents* web_contents, ui::EventType event_type, const gfx::Point& point); void SimulateLongTapAt(WebContents* web_contents, const gfx::Point& point); // Can be used to wait for the caret bounds associated with `web_contents` to // have non-zero size. class NonZeroCaretSizeWaiter { … }; // Can be used to wait for an update to the caret bounds associated with // `web_contents`. class CaretBoundsUpdateWaiter { … }; // Can be used to wait for updates to the bounding box (i.e. the rectangle // enclosing the selection region) associated with `web_contents`. class BoundingBoxUpdateWaiter { … }; #endif // Taps the screen with modifires at |point|. void SimulateTapWithModifiersAt(WebContents* web_contents, unsigned Modifiers, const gfx::Point& point); // Sends a key press asynchronously. // |key| specifies the UIEvents (aka: DOM4Events) value of the key. // |code| specifies the UIEvents (aka: DOM4Events) value of the physical key. // |key_code| alone is good enough for scenarios that only need the char // value represented by a key event and not the physical key on the keyboard // or the keyboard layout. // If set to true, the modifiers |control|, |shift|, |alt|, and |command| are // pressed down first before the key event, and released after. void SimulateKeyPress(WebContents* web_contents, ui::DomKey key, ui::DomCode code, ui::KeyboardCode key_code, bool control, bool shift, bool alt, bool command); // Like SimulateKeyPress(), but does not send the char (AKA keypress) event. // This is useful for arrow keys and other key presses that do not generate // characters. void SimulateKeyPressWithoutChar(WebContents* web_contents, ui::DomKey key, ui::DomCode code, ui::KeyboardCode key_code, bool control, bool shift, bool alt, bool command); // Simulates `source_render_frame_host` sending `message` to // `target_render_frame_host` through a `RenderFrameProxyHost`. This allows // testing corner cases where postMessage() should not be allowed, even from a // compromised renderer. `target_render_frame_host`'s frame must have a // `RenderFrameProxyHost` in `source_render_frame_host`'s `SiteInstanceGroup`. void SimulateProxyHostPostMessage(RenderFrameHost* source_render_frame_host, RenderFrameHost* target_render_frame_host, blink::TransferableMessage message); // Reset touch action for the embedder of a BrowserPluginGuest. void ResetTouchAction(RenderWidgetHost* host); // Spins a run loop until effects of previously forwarded input are fully // realized. void RunUntilInputProcessed(RenderWidgetHost* host); // Returns a string representation of a given |referrer_policy|. This is used to // setup <meta name=referrer> tags in documents used for referrer-policy-based // tests. The value `no-meta` indicates no tag should be created. std::string ReferrerPolicyToString( network::mojom::ReferrerPolicy referrer_policy); // For testing, bind FakeFrameWidget to a RenderWidgetHost associated // with a given RenderFrameHost mojo::PendingAssociatedReceiver<blink::mojom::FrameWidget> BindFakeFrameWidgetInterfaces(RenderFrameHost* frame); // Set |active| state for a RenderWidgetHost associated with a given // RenderFrameHost void SimulateActiveStateForWidget(RenderFrameHost* frame, bool active); // Return the value set for VisitedLinkSalt in the navigation's commit_params. std::optional<uint64_t> GetVisitedLinkSaltForNavigation( NavigationHandle* navigation_handle); // Holds down modifier keys for the duration of its lifetime and releases them // upon destruction. This allows simulating multiple input events without // simulating modifier key releases in between. class ScopedSimulateModifierKeyPress { … }; // Method to check what devices we have on the system. bool IsWebcamAvailableOnSystem(WebContents* web_contents); // Allow ExecJs/EvalJs methods to target either a WebContents or a // RenderFrameHost. Targeting a WebContents means executing the script in the // RenderFrameHost returned by WebContents::GetPrimaryMainFrame(), which is the // main frame. Pass a specific RenderFrameHost to target it. Embedders may // declare additional ConvertToRenderFrameHost functions for convenience. class ToRenderFrameHost { … }; RenderFrameHost* ConvertToRenderFrameHost(RenderFrameHost* render_view_host); RenderFrameHost* ConvertToRenderFrameHost(WebContents* web_contents); // Executes the passed |script| in the specified frame with a user gesture, // without waiting for |script| completion. // // See also: // - ExecJs (if you want to wait for script completion, or detect syntax errors) // - EvalJs (if you want to retrieve a value) // - DOMMessageQueue (to manually wait for domAutomationController.send(...)) void ExecuteScriptAsync(const ToRenderFrameHost& adapter, std::string_view script); // Same as `content::ExecuteScriptAsync()`, but doesn't send a user gesture to // the renderer. void ExecuteScriptAsyncWithoutUserGesture(const ToRenderFrameHost& adapter, std::string_view script); // JsLiteralHelper is a helper class that determines what types are legal to // pass to StringifyJsLiteral. Legal types include int, string, // std::string_view, char*, bool, double, GURL, url::Origin, and base::Value&&. template <typename T> struct JsLiteralHelper { … }; // Specialization allowing GURL to be passed to StringifyJsLiteral. template <> struct JsLiteralHelper<GURL> { … }; // Specialization allowing url::Origin to be passed to StringifyJsLiteral. template <> struct JsLiteralHelper<url::Origin> { … }; // Construct a list-type base::Value from a mix of arguments. // // Each |arg| can be any type explicitly convertible to base::Value // (including int/string/std::string_view/char*/double/bool), or any type that // JsLiteralHelper is specialized for -- like URL and url::Origin, which emit // string literals. |args| can be a mix of different types. template <typename... Args> base::Value ListValueOf(Args&&... args) { … } // Replaces $1, $2, $3, ..., $9 in |script_template| with JS literal values // constructed from |args|, similar to base::ReplaceStringPlaceholders. Note // that $10 and above aren't allowed. // // Unlike StringPrintf or manual concatenation, this version will properly // escape string content, even if it contains slashes or quotation marks. // // Each |arg| can be any type explicitly convertible to base::Value // (including int/string/std::string_view/char*/double/bool), or any type that // JsLiteralHelper is specialized for -- like URL and url::Origin, which emit // string literals. |args| can be a mix of different types. // // Example 1: // // GURL page_url("http://example.com"); // EXPECT_TRUE(ExecJs( // shell(), JsReplace("window.open($1, '_blank');", page_url))); // // $1 is replaced with a double-quoted JS string literal: // "http://example.com". Note that quotes around $1 are not required. // // Example 2: // // bool forced_reload = true; // EXPECT_TRUE(ExecJs( // shell(), JsReplace("window.location.reload($1);", forced_reload))); // // This becomes "window.location.reload(true);" -- because bool values are // supported by base::Value. Numbers, lists, and dicts also work. template <typename... Args> std::string JsReplace(std::string_view script_template, Args&&... args) { … } // The return value of EvalJs. Captures the value (or the error) arising from // script execution. When used with gtest assertions, EvalJsResult generally // behaves like its wrapped value. // // An EvalJsResult can be consumed in two ways: // // (1) [preferred] Pass it directly to an EXPECT_EQ() macro. It has // overloaded operator== against std::string, bool, int, double, // nullptr_t, and base::Value. This will produce readable assertion // failures if there is a type mismatch, or if an exception was thrown -- // errors are never equal to anything. // // For boolean results, note that EXPECT_TRUE(..) and EXPECT_FALSE() // won't compile; use EXPECT_EQ(true, ...) instead. This is intentional, // since EXPECT_TRUE() could be read ambiguously as either "expect // successful execution", "expect truthy value of any type", or "expect // boolean value 'true'". // // (2) [use when necessary] Extract the underlying value of an expected type, // by calling ExtractString(), ExtractInt(), etc. This will produce a // CHECK failure if the execution didn't result in the appropriate type // of result, or if an exception was thrown. struct EvalJsResult { … }; // Enables EvalJsResult to be used directly in ASSERT/EXPECT macros: // // ASSERT_EQ("ab", EvalJs(rfh, "'a' + 'b'")) // ASSERT_EQ(2, EvalJs(rfh, "1 + 1")) // ASSERT_EQ(nullptr, EvalJs(rfh, "var a = 1 + 1")) // // Error values never return true for any comparison operator. template <typename T> bool operator==(const T& a, const EvalJsResult& b) { … } template <typename T> bool operator==(const EvalJsResult& a, const T& b) { … } template <typename T> bool operator!=(const T& a, const EvalJsResult& b) { … } template <typename T> bool operator!=(const EvalJsResult& a, const T& b) { … } template <typename T> bool operator>=(const T& a, const EvalJsResult& b) { … } template <typename T> bool operator>=(const EvalJsResult& a, const T& b) { … } template <typename T> bool operator<=(const T& a, const EvalJsResult& b) { … } template <typename T> bool operator<=(const EvalJsResult& a, const T& b) { … } template <typename T> bool operator<(const T& a, const EvalJsResult& b) { … } template <typename T> bool operator<(const EvalJsResult& a, const T& b) { … } template <typename T> bool operator>(const T& a, const EvalJsResult& b) { … } template <typename T> bool operator>(const EvalJsResult& a, const T& b) { … } inline bool operator==(std::nullptr_t a, const EvalJsResult& b) { … } template <typename T> inline bool operator==(const EvalJsResult& a, std::nullptr_t b) { … } // Provides informative failure messages when the result of EvalJs() is // used in a failing ASSERT_EQ or EXPECT_EQ. std::ostream& operator<<(std::ostream& os, const EvalJsResult& bar); enum EvalJsOptions { … }; // EvalJs() -- run |script| in |execution_target| and return its value or error. // // Example simple usage: // // EXPECT_EQ("https://abcd.com", EvalJs(render_frame_host, "self.origin")); // EXPECT_EQ(5, EvalJs(render_frame_host, "history.length")); // EXPECT_EQ(false, EvalJs(render_frame_host, "history.length > 5")); // // The result value of |script| is its "statement completion value" -- the same // semantics used by Javascript's own eval() function. If |script| // raises exceptions, or is syntactically invalid, an error is captured instead, // including a full stack trace. // // The return value of EvalJs() may be used directly in EXPECT_EQ() // macros, and compared for against std::string, int, or any other type for // which base::Value has a constructor. If an error was thrown by the script, // any comparison operators will always return false. // // If |script|'s captured completion value is a Promise, this function blocks // until the Promise is resolved. This enables a usage pattern where |script| // may call an async function, and use the await keyword to wait for // events to fire. For example: // // EXPECT_EQ(200, EvalJs(rfh, "(async () => { var resp = (await fetch(url));" // " return resp.status; })()"); // // In the above example, the immediately-invoked function expression results in // a Promise (that's what async functions do); EvalJs will continue blocking // until the Promise resolves, which happens when the async function returns // the HTTP status code -- which is expected, in this case, to be 200. // // - If possible, pass the result of EvalJs() into the second argument of an // EXPECT_EQ macro. This will trigger failure (and a nice message) if an // error occurs. // - JS exceptions are reliably captured and will appear as C++ assertion // failures. // - JS stack traces arising from exceptions are annotated with the // corresponding source code; this also appears in C++ assertion failures. // - Delayed response is supported via Promises and JS async/await. // - |script| doesn't need to call domAutomationController.send directly. // - When a script doesn't produce a result, it's likely an assertion // failure rather than a hang. Doesn't get confused by crosstalk with // callers of domAutomationController.send() -- EvalJs does not rely on // domAutomationController. // - Lists, dicts, null values, etc. can be returned as base::Values. // - |after_script_invoke| is an optional callback which will be invoked after // script execution has started in the renderer but before the RunLoop is // blocked on the result. // // It is guaranteed that EvalJs works even when the target frame is frozen. [[nodiscard]] EvalJsResult EvalJs( const ToRenderFrameHost& execution_target, std::string_view script, int options = EXECUTE_SCRIPT_DEFAULT_OPTIONS, int32_t world_id = ISOLATED_WORLD_ID_GLOBAL, base::OnceClosure after_script_invoke = base::DoNothing()); // Like EvalJs(), but runs |raf_script| inside a requestAnimationFrame handler, // and runs |script| after the rendering update has completed. By the time // this method returns, any IPCs sent from the renderer process to the browser // process during the lifecycle update should already have been received and // processed by the browser. [[nodiscard]] EvalJsResult EvalJsAfterLifecycleUpdate( const ToRenderFrameHost& execution_target, std::string_view raf_script, std::string_view script, int options = EXECUTE_SCRIPT_DEFAULT_OPTIONS, int32_t world_id = ISOLATED_WORLD_ID_GLOBAL); // Run a script exactly the same as EvalJs(), but ignore the resulting value. // // Returns AssertionSuccess() if |script| ran successfully, and // AssertionFailure() if |script| contained a syntax error or threw an // exception. // // As with EvalJs(), if the script passed evaluates to a Promise, this waits // until it resolves (by default). [[nodiscard]] ::testing::AssertionResult ExecJs( const ToRenderFrameHost& execution_target, std::string_view script, int options = EXECUTE_SCRIPT_DEFAULT_OPTIONS, int32_t world_id = ISOLATED_WORLD_ID_GLOBAL); // Walks the frame tree of the specified `page`, also descending into any inner // frame-trees (e.g. GuestView), and returns the sole frame that matches the // specified predicate function. Returns nullptr if no frame matches. // EXPECTs that at most one frame matches. RenderFrameHost* FrameMatchingPredicateOrNullptr( Page& page, base::RepeatingCallback<bool(RenderFrameHost*)> predicate); // Same like FrameMatchingPredicateOrNullptr(), but EXPECTs that exactly one // frame matches. RenderFrameHost* FrameMatchingPredicate( Page& page, base::RepeatingCallback<bool(RenderFrameHost*)> predicate); // Predicates for use with FrameMatchingPredicate[OrNullPtr](). bool FrameMatchesName(std::string_view name, RenderFrameHost* frame); bool FrameIsChildOfMainFrame(RenderFrameHost* frame); bool FrameHasSourceUrl(const GURL& url, RenderFrameHost* frame); // Finds the child frame at the specified |index| for |adapter| and returns its // RenderFrameHost. Returns nullptr if such child frame does not exist. RenderFrameHost* ChildFrameAt(const ToRenderFrameHost& adapter, size_t index); // Returns true if |frame| has origin-keyed process isolation due to the // OriginAgentCluster header. bool HasOriginKeyedProcess(RenderFrameHost* frame); // Returns true if `frame` has a sandboxed SiteInstance. bool HasSandboxedSiteInstance(RenderFrameHost* frame); // Returns the frames visited by |RenderFrameHost::ForEachRenderFrameHost| in // the same order. std::vector<RenderFrameHost*> CollectAllRenderFrameHosts( RenderFrameHost* starting_rfh); // Returns the frames visited by |RenderFrameHost::ForEachRenderFrameHost| on // |page|'s main document in the same order. std::vector<RenderFrameHost*> CollectAllRenderFrameHosts(Page& page); // Returns the frames visited by |WebContents::ForEachRenderFrameHost| in // the same order. std::vector<RenderFrameHost*> CollectAllRenderFrameHosts( WebContents* web_contents); // Returns all WebContents. Note that this includes WebContents from any // BrowserContext. std::vector<WebContents*> GetAllWebContents(); #if BUILDFLAG(IS_CHROMEOS_ASH) // Executes the WebUI resource tests. Injects the test runner script prior to // executing the tests. // // Returns true if tests ran successfully, false otherwise. bool ExecuteWebUIResourceTest(WebContents* web_contents); #endif // Returns the serialized cookie string for the given url. Uses an inclusive // SameSiteCookieContext by default, which gets cookies regardless of their // SameSite attribute. std::string GetCookies( BrowserContext* browser_context, const GURL& url, net::CookieOptions::SameSiteCookieContext context = net::CookieOptions::SameSiteCookieContext::MakeInclusive(), net::CookiePartitionKeyCollection key_collection = net::CookiePartitionKeyCollection::ContainsAll()); // Returns the canonical cookies for the given url. std::vector<net::CanonicalCookie> GetCanonicalCookies( BrowserContext* browser_context, const GURL& url, net::CookiePartitionKeyCollection key_collection = net::CookiePartitionKeyCollection::ContainsAll()); // Sets a cookie for the given url. Uses inclusive SameSiteCookieContext by // default, which gets cookies regardless of their SameSite attribute. The // cookie is unpartitioned by default. Returns true on success. bool SetCookie(BrowserContext* browser_context, const GURL& url, const std::string& value, net::CookieOptions::SameSiteCookieContext context = net::CookieOptions::SameSiteCookieContext::MakeInclusive(), net::CookiePartitionKey* cookie_partition_key = nullptr); // Deletes cookies matching the provided filter. Returns the number of cookies // that were deleted. uint32_t DeleteCookies(BrowserContext* browser_context, network::mojom::CookieDeletionFilter filter); // Fetches the histograms data from other processes. // // This function should be called after a child process has logged the // histogram/metric being tested, to ensure that base::HistogramTester sees all // the data from the child process. // // The caller should ensure that there is no race between 1) the call to // FetchHistogramsFromChildProcesses() and 2) the child process shutting down. // See also https://crbug.com/1246137 which describes how this is a test-only // problem. void FetchHistogramsFromChildProcesses(); // Registers a request handler which redirects to a different host, based // on the request path. The format of the path should be // "/cross-site/hostname/rest/of/path" to redirect the request to // "<scheme>://hostname:<port>/rest/of/path", where <scheme> and <port> // are the values for the instance of EmbeddedTestServer. // // By default, redirection will be done using HTTP 302 response, but in some // cases (e.g. to preserve HTTP method and POST body across redirects as // prescribed by https://tools.ietf.org/html/rfc7231#section-6.4.7) a test might // want to use HTTP 307 response instead. This can be accomplished by replacing // "/cross-site/" URL substring above with "/cross-site-307/". // // |embedded_test_server| should not be running when passing it to this function // because adding the request handler won't be thread safe. void SetupCrossSiteRedirector(net::EmbeddedTestServer* embedded_test_server); // Sets the access permission context in FileSystemAccessManagerImpl. void SetFileSystemAccessPermissionContext( BrowserContext* browser_context, FileSystemAccessPermissionContext* permission_context); // Waits until all resources have loaded in the given RenderFrameHost. [[nodiscard]] bool WaitForRenderFrameReady(RenderFrameHost* rfh); // Wait until the focused accessible node changes in any WebContents. void WaitForAccessibilityFocusChange(); // Retrieve information about the node that's focused in the accessibility tree. ui::AXNodeData GetFocusedAccessibilityNodeInfo(WebContents* web_contents); // This is intended to be a robust way to assert that the accessibility // tree eventually gets into the correct state, without worrying about // the exact ordering of events received while getting there. Blocks // until any change happens to the accessibility tree. void WaitForAccessibilityTreeToChange(WebContents* web_contents); // Searches the accessibility tree to see if any node's accessible name // is equal to the given name. If not, repeatedly calls // WaitForAccessibilityTreeToChange, above, and then checks again. // Keeps looping until the text is found (or the test times out). void WaitForAccessibilityTreeToContainNodeWithName(WebContents* web_contents, std::string_view name); // Get a snapshot of a web page's accessibility tree. ui::AXTreeUpdate GetAccessibilityTreeSnapshot(WebContents* web_contents); // Get a snapshot of an accessibility tree given a `tree_id`. ui::AXTreeUpdate GetAccessibilityTreeSnapshotFromId( const ui::AXTreeID& tree_id); // Returns the root accessibility node for the given WebContents. ui::AXPlatformNodeDelegate* GetRootAccessibilityNode(WebContents* web_contents); // Finds an accessibility node matching the given criteria. struct FindAccessibilityNodeCriteria { … }; ui::AXPlatformNodeDelegate* FindAccessibilityNode( WebContents* web_contents, const FindAccessibilityNodeCriteria& criteria); ui::AXPlatformNodeDelegate* FindAccessibilityNodeInSubtree( ui::AXPlatformNodeDelegate* node, const FindAccessibilityNodeCriteria& criteria); #if BUILDFLAG(IS_WIN) // Retrieve the specified interface from an accessibility node. template <typename T> Microsoft::WRL::ComPtr<T> QueryInterfaceFromNode( ui::AXPlatformNodeDelegate* node); // Call GetPropertyValue with the given UIA property id with variant type // VT_ARRAY | VT_UNKNOWN on the target browser accessibility node to retrieve // an array of automation elements, then validate the name property of the // automation elements with the expected names. void UiaGetPropertyValueVtArrayVtUnknownValidate( PROPERTYID property_id, ui::AXPlatformNodeDelegate* target_node, const std::vector<std::string>& expected_names); #endif // Returns the RenderWidgetHost that holds the keyboard lock. RenderWidgetHost* GetKeyboardLockWidget(WebContents* web_contents); // Returns the RenderWidgetHost that holds the mouse lock. RenderWidgetHost* GetMouseLockWidget(WebContents* web_contents); // Allows tests to drive keyboard lock functionality without requiring access // to the RenderWidgetHostImpl header or setting up an HTTP test server. // |codes| represents the set of keys to lock. If |codes| has no value, then // all keys will be considered locked. If |codes| has a value, then at least // one key must be specified. void RequestKeyboardLock( WebContents* web_contents, std::optional<base::flat_set<ui::DomCode>> codes, base::OnceCallback<void(blink::mojom::KeyboardLockRequestResult)> callback); void CancelKeyboardLock(WebContents* web_contents); // Returns the screen orientation provider that's been set via // WebContents::SetScreenOrientationDelegate(). May return null. ScreenOrientationDelegate* GetScreenOrientationDelegate(); // Returns all the RenderWidgetHostViews inside the |web_contents| that are // registered in the RenderWidgetHostInputEventRouter. std::vector<RenderWidgetHostView*> GetInputEventRouterRenderWidgetHostViews( WebContents* web_contents); // Returns the focused RenderWidgetHost. RenderWidgetHost* GetFocusedRenderWidgetHost(WebContents* web_contents); // Returns whether or not the RenderWidgetHost thinks it is focused. bool IsRenderWidgetHostFocused(const RenderWidgetHost*); // Returns the focused WebContents. WebContents* GetFocusedWebContents(WebContents* web_contents); // Watches title changes on a WebContents, blocking until an expected title is // set. class TitleWatcher : public WebContentsObserver { … }; // Watches a RenderProcessHost and waits for a specified lifecycle event. class RenderProcessHostWatcher : public RenderProcessHostObserver { … }; // Implementation helper for: // *) content-internal content::RenderProcessHostBadIpcMessageWaiter // (declared in //content/test/content_browser_test_utils_internal.h) // *) content-public content::RenderProcessHostBadMojoMessageWaiter // (declared below) // *) maybe in the future: similar helpers for chrome-layer BadMessageReason class RenderProcessHostKillWaiter { … }; // Helps tests to wait until the given renderer process is terminated because of // a bad/invalid mojo message. // // Example usage: // RenderProcessHostBadMojoMessageWaiter kill_waiter(render_process_host); // ... test code that triggers a renderer kill ... // EXPECT_EQ("expected error message", kill_waiter.Wait()); class RenderProcessHostBadMojoMessageWaiter { … }; // Watches for responses from the DOMAutomationController and keeps them in a // queue. Useful for waiting for a message to be received. class DOMMessageQueue { … }; // Used to wait for a new WebContents to be created. Instantiate this object // before the operation that will create the window. class WebContentsAddedObserver { … }; // Request a new frame be drawn, returns false if request fails. bool RequestFrame(WebContents* web_contents); // This class is intended to synchronize upon the submission of compositor // frames from the renderer to the display compositor. // // This class enables observation of the provided // RenderFrameMetadataProvider. Which notifies this of every // subsequent frame submission. Observation ends upon the destruction of this // class. // // Calling Wait will block the browser ui thread until the next time the // renderer submits a frame. // // Tests interested in the associated RenderFrameMetadata will find it cached // in the RenderFrameMetadataProvider. class RenderFrameSubmissionObserver : public RenderFrameMetadataProvider::Observer { … }; // This class is intended to synchronize the renderer main thread, renderer impl // thread and the browser main thread. // // This is accomplished by sending an IPC to RenderWidget, then blocking until // the ACK is received and processed. // // The ACK is sent from compositor thread, when the CompositorFrame is submited // to the display compositor // TODO(danakj): This class seems to provide the same as // RenderFrameSubmissionObserver, consider using that instead. class MainThreadFrameObserver { … }; // Watches for an input msg to be consumed. class InputMsgWatcher : public RenderWidgetHost::InputEventObserver { … }; // Used to wait for a desired input event ack. class InputEventAckWaiter : public RenderWidgetHost::InputEventObserver { … }; // Sets up a ui::TestClipboard for use in browser tests. On Windows, // clipboard is handled on the IO thread, BrowserTestClipboardScope // hops messages onto the right thread. class BrowserTestClipboardScope { … }; // This observer is used to wait for its owner Frame to become focused. class FrameFocusedObserver { … }; // This observer is used to wait for its owner FrameTreeNode to become deleted. class FrameDeletedObserver { … }; // This class can be used to pause and resume navigations, based on a URL // match. Note that it only keeps track of one navigation at a time. // Navigations are paused automatically before hitting the network, and are // resumed automatically if a Wait method is called for a future event. // // Note: This class is one time use only! After it successfully tracks a // navigation it will ignore all subsequent navigations. Explicitly create // multiple instances of this class if you want to pause multiple navigations. // // Note2: This class cannot be used with page activating navigations (e.g. // BFCache, prerendering, etc.) as the activation doesn't run // NavigationThrottles. Use TestActivationManager in these cases instead. class TestNavigationManager : public WebContentsObserver { … }; // Like TestNavigationManager but for page activating navigations like // Back-Forward Cache restores and prerendering activations. This can be used // to pause and resume the navigation at certain points during an activation. // Note that only a single navigation will be tracked by this manager. When // this object is live, a matching navigation will be automatically paused at // kBeforeChecks and resumed automatically when a Wait method is called. class TestActivationManager : public WebContentsObserver { … }; class NavigationHandleCommitObserver : public content::WebContentsObserver { … }; // A test utility that monitors console messages sent to a WebContents. This // can be used to wait for a message that matches a specific filter, an // arbitrary message, or monitor all messages sent to the WebContents' console. class WebContentsConsoleObserver : public WebContentsObserver { … }; // A helper class to get DevTools inspector log messages (e.g. network errors). class DevToolsInspectorLogWatcher : public DevToolsAgentHostClient { … }; // Static methods that simulates Mojo methods as if they were called by a // renderer. Used to simulate a compromised renderer. class PwnMessageHelper { … }; #if defined(USE_AURA) // Tests that a |render_widget_host_view| stores a stale content when its frame // gets evicted. |render_widget_host_view| has to be a RenderWidgetHostViewAura. void VerifyStaleContentOnFrameEviction( RenderWidgetHostView* render_widget_host_view); #endif // defined(USE_AURA) // Helper class to interpose on Blob URL registrations, replacing the URL // contained in incoming registration requests with the specified URL. class BlobURLStoreInterceptor : public blink::mojom::BlobURLStoreInterceptorForTesting { … }; // Load the given |url| with |network_context| and return the |net::Error| code. // // This overload simulates loading through a URLLoaderFactory created for a // Browser process. int LoadBasicRequest(network::mojom::NetworkContext* network_context, const GURL& url, int load_flags = net::LOAD_NORMAL); // Load the given |url| via URLLoaderFactory created by |frame|. Return the // |net::Error| code. // // This overload simulates loading through a URLLoaderFactory created for a // Renderer process (the factory is driven from the Test/Browser process, but // has the same properties as factories vended to the Renderer process that // hosts the |frame|). int LoadBasicRequest(RenderFrameHost* frame, const GURL& url); // Ensures that all StoragePartitions for the given BrowserContext have their // cookies flushed to disk. void EnsureCookiesFlushed(BrowserContext* browser_context); // Performs a simple auto-resize flow and ensures that the embedder gets a // single response messages back from the guest, with the expected values. bool TestGuestAutoresize(WebContents* embedder_web_contents, RenderFrameHost* guest_main_frame); // This class allows monitoring of mouse events received by a specific // RenderWidgetHost. class RenderWidgetHostMouseEventMonitor { … }; // Helper class to track and allow waiting for navigation start events. class DidStartNavigationObserver : public WebContentsObserver { … }; // Tracks the creation of RenderFrameProxyHosts that have // CrossProcessFrameConnectors, and records the initial (post-construction) // device scale factor in the CrossProcessFrameConnector. class ProxyDSFObserver { … }; // Helper used by the wrapper class below. Compares the output of the given // |web_contents| to the PNG file at |expected_path| across the region defined // by |snapshot_size| and returns true if the images are equivalent. Uses a // ManhattanDistancePixelComparator which allows some small differences. If // the flag switches::kRebaselinePixelTests (--rebaseline-pixel-tests) is set, // this function will (over)write the reference file with the produced output. bool CompareWebContentsOutputToReference( WebContents* web_contents, const base::FilePath& expected_path, const gfx::Size& snapshot_size, const cc::PixelComparator& comparator = cc::ManhattanDistancePixelComparator()); RenderFrameHostChangedCallback; // Runs callback at RenderFrameHostChanged time. On cross-RFH navigations, this // will run the callback after the new RenderFrameHost committed and is set as // the current RenderFrameHost, etc. but before the old RenderFrameHost gets // unloaded. class RenderFrameHostChangedCallbackRunner : public WebContentsObserver { … }; // Calls |callback| whenever a navigation finishes in |render_frame_host|. class DidFinishNavigationObserver : public WebContentsObserver { … }; // Wait for a new WebContents to be created, and for it to finish navigation. // It will detect WebContents creation after construction, even if it's before // Wait() is called. The intended pattern is: // // CreateAndLoadWebContentsObserver observer; // ...Do something that creates one WebContents and causes it to navigate... // observer.Wait(); class CreateAndLoadWebContentsObserver { … }; // Waits for the given number of calls to // WebContentsObserver::OnCookiesAccessed. class CookieChangeObserver : public content::WebContentsObserver { … }; // Wait for the creation of Speculative RFH without throttling the navigation. // Since the TestNavigationManager will throttle the navigation, using with // class with TestNavigationManager is not recommended. Manually driving the // run loop will be required to receive the events in both objects. We recommend // to use TestNavigationManager for simply driving the navigation. However if it // is required to intercept the navigation with other observers such as // CommitPauser, it would be better to use SpeculativeRenderFrameHostObserver to // ensure the speculative RFH to avoid interference caused by the // TestNavigationManager. class SpeculativeRenderFrameHostObserver : public content::WebContentsObserver { … }; class SpareRenderProcessObserver { … }; [[nodiscard]] base::CallbackListSubscription RegisterWebContentsCreationCallback( base::RepeatingCallback<void(WebContents*)> callback); // Functions to traverse history and wait until the traversal completes, even if // the loading is stopped halfway (e.g. if a BackForwardCache entry is evicted // during the restoration, causing the old NavigationRequest to be reset and a // new NavigationRequest to be restarted). These are wrappers around the // same-named methods of the `NavigationController`. [[nodiscard]] bool HistoryGoToIndex(WebContents* wc, int index); [[nodiscard]] bool HistoryGoToOffset(WebContents* wc, int offset); [[nodiscard]] bool HistoryGoBack(WebContents* wc); [[nodiscard]] bool HistoryGoForward(WebContents* wc); #if BUILDFLAG(IS_MAC) // Grant native windows the ability to activate, allowing them to become key // and/or main. This can be useful to enable when the process hosting the window // is a standalone executable without an Info.plist. bool EnableNativeWindowActivation(); #endif // BUILDFLAG(IS_MAC) #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) // Set the global factory for CapturedSurfaceController objects. void SetCapturedSurfaceControllerFactoryForTesting( base::RepeatingCallback<std::unique_ptr<MockCapturedSurfaceController>( GlobalRenderFrameHostId, WebContentsMediaCaptureId)> factory); #endif // !BUILDFLAG(IS_ANDROID) } // namespace content #endif // CONTENT_PUBLIC_TEST_BROWSER_TEST_UTILS_H_