blob: ed0c11049f9a892f90de7c1399bbcaa7b380a678 [file] [log] [blame] [edit]
// Copyright 2021 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_PRERENDER_TEST_UTIL_H_
#define CONTENT_PUBLIC_TEST_PRERENDER_TEST_UTIL_H_
#include "base/functional/callback.h"
#include "base/test/scoped_feature_list.h"
#include "content/public/browser/preloading_trigger_type.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/isolated_world_ids.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/preloading_test_util.h"
#include "content/public/test/test_navigation_observer.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/mojom/speculation_rules/speculation_rules.mojom.h"
#include "url/gurl.h"
namespace content {
namespace test {
class PrerenderHostRegistryObserverImpl;
// The PrerenderHostRegistryObserver permits waiting for a host to be created
// for a given URL.
class PrerenderHostRegistryObserver {
public:
explicit PrerenderHostRegistryObserver(WebContents& web_contents);
~PrerenderHostRegistryObserver();
PrerenderHostRegistryObserver(const PrerenderHostRegistryObserver&) = delete;
PrerenderHostRegistryObserver& operator=(
const PrerenderHostRegistryObserver&) = delete;
// Returns immediately if |url| was ever triggered before. Otherwise blocks
// on a RunLoop until a prerender of |url| is triggered.
void WaitForTrigger(const GURL& url);
// Blocks on a RunLoop until a next prerender is triggered. Returns a URL of
// the prerender.
GURL WaitForNextTrigger();
// Invokes |callback| immediately if |url| was ever triggered before.
// Otherwise invokes |callback| when a prerender for |url| is triggered.
void NotifyOnTrigger(const GURL& url, base::OnceClosure callback);
// Returns a set of URLs that have been triggered so far.
base::flat_set<GURL> GetTriggeredUrls() const;
private:
std::unique_ptr<PrerenderHostRegistryObserverImpl> impl_;
};
class PrerenderHostObserverImpl;
// The PrerenderHostObserver permits listening for host activation and
// destruction
class PrerenderHostObserver {
public:
// Begins observing the given PrerenderHost immediately. DCHECKs if |host_id|
// does not identify a live PrerenderHost.
PrerenderHostObserver(WebContents& web_contents, FrameTreeNodeId host_id);
// Will start observing a PrerenderHost for |url| as soon as it is
// triggered.
PrerenderHostObserver(WebContents& web_contents, const GURL& url);
~PrerenderHostObserver();
PrerenderHostObserver(const PrerenderHostObserver&) = delete;
PrerenderHostObserver& operator=(const PrerenderHostObserver&) = delete;
// Returns immediately if the PrerenderHost was already activated, otherwise
// spins a RunLoop until the observed host is activated.
void WaitForActivation();
// Returns immediately if the PrerenderHost has already received headers,
// otherwise spins a RunLoop until the observed host receives headers.
void WaitForHeaders();
// Returns immediately if the PrerenderHost was already destroyed, otherwise
// spins a RunLoop until the observed host is destroyed.
void WaitForDestroyed();
// True if the PrerenderHost was activated to be the primary page.
bool was_activated() const;
private:
std::unique_ptr<PrerenderHostObserverImpl> impl_;
};
// This waits for creation of PrerenderHost and then returns its host id.
class PrerenderHostCreationWaiter {
public:
PrerenderHostCreationWaiter();
~PrerenderHostCreationWaiter() = default;
FrameTreeNodeId Wait();
private:
base::RunLoop run_loop_;
FrameTreeNodeId created_host_id_;
};
// Enables appropriate features for Prerender2.
// This also disables the memory requirement of Prerender2 on Android so that
// test can run on any bot.
class ScopedPrerenderFeatureList {
public:
ScopedPrerenderFeatureList();
explicit ScopedPrerenderFeatureList(bool force_disable_prerender2_fallback,
bool force_enable_prerender2_in_new_tab);
ScopedPrerenderFeatureList(const ScopedPrerenderFeatureList&) = delete;
ScopedPrerenderFeatureList& operator=(const ScopedPrerenderFeatureList&) =
delete;
private:
base::test::ScopedFeatureList feature_list_;
};
// Browser tests can use this class to more conveniently leverage prerendering.
class PrerenderTestHelper {
public:
explicit PrerenderTestHelper(const WebContents::Getter& fn);
explicit PrerenderTestHelper(const WebContents::Getter& fn,
bool force_disable_prerender2_fallback,
bool force_enable_prerender2_in_new_tab);
~PrerenderTestHelper();
PrerenderTestHelper(const PrerenderTestHelper&) = delete;
PrerenderTestHelper& operator=(const PrerenderTestHelper&) = delete;
// This installs a network monitor on the http server. Be sure to call this
// before starting the server. This is typically done from SetUp, but it is
// fine to call from SetUpOnMainThread if ordering constraints make that
// impossible (eg, if the test helper is created later to avoid problematic
// creation/destruction relative to other ScopedFeatureLists or if the fixture
// creates test server after SetUp).
void RegisterServerRequestMonitor(
net::test_server::EmbeddedTestServer* http_server);
void RegisterServerRequestMonitor(
net::test_server::EmbeddedTestServer& test_server);
// Attempts to lookup the host for the given |url|. Returns an invalid frame
// id upon failure.
static FrameTreeNodeId GetHostForUrl(WebContents& web_contents,
const GURL& url);
FrameTreeNodeId GetHostForUrl(const GURL& url);
static FrameTreeNodeId GetPrewarmSearchResultHost(WebContents& web_contents,
const GURL& prewarm_url);
FrameTreeNodeId GetPrewarmSearchResultHost(const GURL& prewarm_url);
// Returns whether the registry holds the handler for prerender-into-new-tab.
bool HasNewTabHandle(FrameTreeNodeId host_id);
// Waits until a prerender has finished loading.
//
// - `WaitForPrerenderLoadCompletion()` waits for
// `PrerenderHost::LoadingOutcome::kLoadingCompleted`. Note: this may not be
// called when the load fails (e.g. because it was blocked by a
// NavigationThrottle, or the WebContents is destroyed).
// - `WaitForPrerenderLoadCancellation()` waits for
// `PrerenderHost::LoadingOutcome::kPrerenderingCancelled`.
//
// If the prerender doesn't yet exist, these will wait until it is triggered.
static void WaitForPrerenderLoadCompletion(WebContents& web_contents,
const GURL& url);
void WaitForPrerenderLoadCompletion(const GURL& url);
void WaitForPrerenderLoadCompletion(FrameTreeNodeId host_id);
static void WaitForPrerenderLoadCancellation(WebContents& web_contents,
const GURL& url);
void WaitForPrerenderLoadCancellation(const GURL& url);
void WaitForPrerenderLoadCancellation(FrameTreeNodeId host_id);
// Adds <script type="speculationrules"> in the current main frame and waits
// until the completion of prerendering. Returns the id of the resulting
// prerendering host.
FrameTreeNodeId AddPrerender(const GURL& prerendering_url,
int32_t world_id = ISOLATED_WORLD_ID_GLOBAL);
FrameTreeNodeId AddPrerender(
const GURL& prerendering_url,
std::optional<blink::mojom::SpeculationEagerness> eagerness,
const std::string& target_hint,
int32_t world_id = ISOLATED_WORLD_ID_GLOBAL);
FrameTreeNodeId AddPrerender(
const GURL& prerendering_url,
std::optional<blink::mojom::SpeculationEagerness> eagerness,
std::optional<std::string> no_vary_search_hint,
const std::string& target_hint,
std::optional<std::string> ruleset_tag = std::nullopt,
int32_t world_id = ISOLATED_WORLD_ID_GLOBAL);
// AddPrerenderAsync() is the same as AddPrerender(), but does not wait until
// the completion of prerendering.
void AddPrerenderAsync(const GURL& prerendering_url,
int32_t world_id = ISOLATED_WORLD_ID_GLOBAL);
void AddPrerendersAsync(
const std::vector<GURL>& prerendering_urls,
std::optional<blink::mojom::SpeculationEagerness> eagerness,
const std::string& target_hint,
int32_t world_id = ISOLATED_WORLD_ID_GLOBAL);
void AddPrerendersAsync(
const std::vector<GURL>& prerendering_urls,
std::optional<blink::mojom::SpeculationEagerness> eagerness,
std::optional<std::string> no_vary_search_hint,
const std::string& target_hint,
std::optional<std::string> ruleset_tag = std::nullopt,
int32_t world_id = ISOLATED_WORLD_ID_GLOBAL);
void AddPrefetchAsync(const GURL& prefetch_url);
// Starts prerendering and returns a PrerenderHandle that should be kept alive
// until prerender activation. Note that it returns before the completion of
// the prerendering navigation.
static std::unique_ptr<PrerenderHandle> AddEmbedderTriggeredPrerenderAsync(
WebContents& web_contents,
const GURL& prerendering_url,
PreloadingTriggerType trigger_type,
const std::string& embedder_histogram_suffix,
ui::PageTransition page_transition);
// This is the same as the previous one, but uses WebContents owned by this
// test helper.
std::unique_ptr<PrerenderHandle> AddEmbedderTriggeredPrerenderAsync(
const GURL& prerendering_url,
PreloadingTriggerType trigger_type,
const std::string& embedder_histogram_suffix,
ui::PageTransition page_transition);
// This navigates, but does not activate, the prerendered page.
void NavigatePrerenderedPage(FrameTreeNodeId host_id, const GURL& url);
// This cancels the prerendered page.
void CancelPrerenderedPage(FrameTreeNodeId host_id);
// Navigates the primary page to the URL and waits until the completion of
// the navigation.
//
// Navigations that could activate a prerendered page should use this function
// instead of the NavigateToURL() test helper. This is because the test helper
// could access a navigating frame being destroyed during activation and fail.
// If `transition` is PAGE_TRANSITION_LINK, it is treated as renderer
// initiated, and this function waits for any ongoing navigation to complete
// before navigating to `url`.
static void NavigatePrimaryPage(
WebContents& web_contents,
const GURL& url,
ui::PageTransition transition = ui::PAGE_TRANSITION_LINK);
void NavigatePrimaryPage(
const GURL& url,
ui::PageTransition transition = ui::PAGE_TRANSITION_LINK);
// Navigates the primary page to the URL but does not wait until the
// completion of the navigation. Instead it returns a
// content::TestNavigationObserver.
// If `transition` is PAGE_TRANSITION_LINK, it is treated as renderer
// initiated, and this function waits for any ongoing navigation to complete
// before navigating to `url`.
static std::unique_ptr<content::TestNavigationObserver>
NavigatePrimaryPageAsync(
WebContents& web_contents,
const GURL& url,
ui::PageTransition transition = ui::PAGE_TRANSITION_LINK);
std::unique_ptr<content::TestNavigationObserver> NavigatePrimaryPageAsync(
const GURL& url,
ui::PageTransition transition = ui::PAGE_TRANSITION_LINK);
// Opens a new window without an opener on the primary page of `web_contents`.
// This is intended for activating a prerendered page initiated for a new
// window.
static void OpenNewWindowWithoutOpener(WebContents& web_contents,
const GURL& url);
// Confirms that, internally, appropriate subframes report that they are
// prerendering (and that each frame tree type is kPrerender).
[[nodiscard]] ::testing::AssertionResult VerifyPrerenderingState(
const GURL& url);
// Returns RenderFrameHost corresponding to `host_id` or `url`.
static RenderFrameHost* GetPrerenderedMainFrameHost(WebContents& web_contents,
FrameTreeNodeId host_id);
static RenderFrameHost* GetPrerenderedMainFrameHost(WebContents& web_contents,
const GURL& url);
RenderFrameHost* GetPrerenderedMainFrameHost(FrameTreeNodeId host_id);
RenderFrameHost* GetPrerenderedMainFrameHost(const GURL& url);
int GetRequestCount(const GURL& url);
net::test_server::HttpRequest::HeaderMap GetRequestHeaders(const GURL& url);
// Waits until the request count for `url` reaches `count`.
void WaitForRequest(const GURL& url, int count);
// Generates the histogram name by appending the trigger type and the embedder
// suffix to the base name.
std::string GenerateHistogramName(const std::string& histogram_base_name,
content::PreloadingTriggerType trigger_type,
const std::string& embedder_suffix);
// Updates the settings in PreloadingConfigOverride.
void SetHoldback(PreloadingType preloading_type,
PreloadingPredictor predictor,
bool holdback);
void SetHoldback(std::string_view preloading_type,
std::string_view predictor,
bool holdback);
private:
void MonitorResourceRequest(const net::test_server::HttpRequest& request);
WebContents* GetWebContents();
// Counts of requests sent to the server. Keyed by path (not by full URL)
// because the host part of the requests is translated ("a.test" to
// "127.0.0.1") before the server handles them.
// This is accessed from the UI thread and `EmbeddedTestServer::io_thread_`.
std::map<std::string, int> request_count_by_path_ GUARDED_BY(lock_);
std::map<std::string, net::test_server::HttpRequest::HeaderMap>
request_headers_by_path_ GUARDED_BY(lock_);
ScopedPrerenderFeatureList feature_list_;
base::OnceClosure monitor_callback_ GUARDED_BY(lock_);
base::Lock lock_;
PreloadingConfigOverride preloading_config_override_;
WebContents::Getter get_web_contents_fn_;
};
// This test delegate is used for prerender-tests, in order to support
// prerendering going through the WebContentsDelegate.
class ScopedPrerenderWebContentsDelegate : public WebContentsDelegate {
public:
explicit ScopedPrerenderWebContentsDelegate(WebContents& web_contents);
~ScopedPrerenderWebContentsDelegate() override;
// WebContentsDelegate override.
PreloadingEligibility IsPrerender2Supported(
WebContents& web_contents,
PreloadingTriggerType trigger_type) override;
private:
base::WeakPtr<WebContents> web_contents_;
};
// This test delegate is used for link preview tests, in order to check
// whether the delegate receives `InitiatePreview` function call.
class MockLinkPreviewWebContentsDelegate : public WebContentsDelegate {
public:
MockLinkPreviewWebContentsDelegate();
MockLinkPreviewWebContentsDelegate(
const MockLinkPreviewWebContentsDelegate&) = delete;
MockLinkPreviewWebContentsDelegate& operator=(
const MockLinkPreviewWebContentsDelegate&) = delete;
~MockLinkPreviewWebContentsDelegate() override;
MOCK_METHOD(void,
InitiatePreview,
(WebContents & web_contents, const GURL& url),
(override));
};
} // namespace test
} // namespace content
#endif // CONTENT_PUBLIC_TEST_PRERENDER_TEST_UTIL_H_