1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
content / public / test / prerender_test_util.h [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 "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();
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 |url|. Returns an invalid frame
// id upon failure.
static FrameTreeNodeId GetHostForUrl(WebContents& web_contents,
const GURL& url);
FrameTreeNodeId GetHostForUrl(const GURL& 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);
// 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,
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,
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(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.
static void NavigatePrimaryPage(WebContents& web_contents, const GURL& url);
void NavigatePrimaryPage(const GURL& url);
// Navigates the primary page to the URL but does not wait until the
// completion of the navigation. Instead it returns a
// content::TestNavigationObserver.
static std::unique_ptr<content::TestNavigationObserver>
NavigatePrimaryPageAsync(WebContents& web_contents, const GURL& url);
std::unique_ptr<content::TestNavigationObserver> NavigatePrimaryPageAsync(
const GURL& url);
// 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) 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_