blob: b4180f30db96abcd53a0d54aff005ed26e305b77 [file] [log] [blame]
// 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 "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.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 |gurl| was ever triggered before. Otherwise blocks
// on a RunLoop until a prerender of |gurl| is triggered.
void WaitForTrigger(const GURL& gurl);
// Invokes |callback| immediately if |gurl| was ever triggered before.
// Otherwise invokes |callback| when a prerender for |gurl| is triggered.
void NotifyOnTrigger(const GURL& gurl, base::OnceClosure callback);
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, int host_id);
// Will start observing a PrerenderHost for |gurl| as soon as it is
// triggered.
PrerenderHostObserver(WebContents& web_contents, const GURL& gurl);
~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 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;
int Wait();
private:
base::RunLoop run_loop_;
int created_host_id_ = content::RenderFrameHost::kNoFrameTreeNodeId;
};
// 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();
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);
~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 |gurl|. Returns
// RenderFrameHost::kNoFrameTreeNodeId upon failure.
static int GetHostForUrl(WebContents& web_contents, const GURL& gurl);
int GetHostForUrl(const GURL& gurl);
// Returns whether the registry holds the handler for prerender-into-new-tab.
bool HasNewTabHandle(int host_id);
// Waits until a prerender has finished loading. Note: this may not be called
// when the load fails (e.g. because it was blocked by a NavigationThrottle,
// or the WebContents is destroyed). If the prerender doesn't yet exist, this
// will wait until it is triggered.
static void WaitForPrerenderLoadCompletion(WebContents& web_contents,
const GURL& gurl);
void WaitForPrerenderLoadCompletion(const GURL& gurl);
void WaitForPrerenderLoadCompletion(int 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.
int AddPrerender(const GURL& prerendering_url,
int32_t world_id = ISOLATED_WORLD_ID_GLOBAL);
int AddPrerender(const GURL& prerendering_url,
std::optional<blink::mojom::SpeculationEagerness> eagerness,
const std::string& target_hint,
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 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.
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(int host_id, const GURL& gurl);
// This cancels the prerendered page.
void CancelPrerenderedPage(int 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.
static void NavigatePrimaryPage(WebContents& web_contents, const GURL& gurl);
void NavigatePrimaryPage(const GURL& gurl);
// 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& gurl);
// Returns RenderFrameHost corresponding to `host_id`.
static RenderFrameHost* GetPrerenderedMainFrameHost(WebContents& web_contents,
int host_id);
RenderFrameHost* GetPrerenderedMainFrameHost(int host_id);
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& gurl, 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);
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_;
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) override;
private:
base::WeakPtr<WebContents> web_contents_;
};
} // namespace test
} // namespace content
#endif // CONTENT_PUBLIC_TEST_PRERENDER_TEST_UTIL_H_