0

prerender: Add a same-site cross-origin speculation tags test

This CL adds a test verifying speculation rules tags header field is
sent in same-site cross-origin situation.

See the section in the related spec:
https://github.com/WICG/nav-speculation/blob/main/speculation-rules-tags.md#the-cross-site-case

Bug: 381687257
Change-Id: I4aa56054934c7a132dcee460fe099e804b4833c4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6433267
Commit-Queue: Huanpo Lin <robertlin@chromium.org>
Reviewed-by: Hiroki Nakagawa <nhiroki@chromium.org>
Reviewed-by: Domenic Denicola <domenic@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1444507}
This commit is contained in:
Robert Lin
2025-04-08 19:22:14 -07:00
committed by Chromium LUCI CQ
parent 254252eb57
commit 3c0b3f803d
3 changed files with 57 additions and 9 deletions

@ -428,12 +428,20 @@ class PrerenderBrowserTest : public ContentBrowserTest,
return prerender_helper_->AddPrerender(prerendering_url, world_id);
}
FrameTreeNodeId AddPrerenderWithTags(const GURL& prerendering_url,
std::optional<std::string> tag) {
return prerender_helper_->AddPrerender(
prerendering_url, /*eagerness=*/std::nullopt,
/*no_vary_search_hint=*/std::string(),
/*target_hint=*/std::string(), tag);
}
FrameTreeNodeId AddPrerender(const GURL& prerendering_url,
std::string no_vary_search_hint,
int32_t world_id = ISOLATED_WORLD_ID_GLOBAL) {
return prerender_helper_->AddPrerender(
prerendering_url, /*eagerness=*/std::nullopt, no_vary_search_hint,
/*target_hint=*/std::string(), world_id);
/*target_hint=*/std::string(), /*ruleset_tag=*/std::nullopt, world_id);
}
void AddPrerenderAsync(const GURL& prerendering_url) {
@ -12775,6 +12783,26 @@ IN_PROC_BROWSER_TEST_P(PrerenderRequestHeadersBrowserTest,
}
}
IN_PROC_BROWSER_TEST_P(PrerenderRequestHeadersBrowserTest,
SpeculationRulesTagForSameSiteCrossOrigin) {
const GURL initial_url = GetUrl("/prerender/empty.html");
const GURL prerender_url =
GetSameSiteCrossOriginUrl("/prerender/prerender_with_opt_in_header.html");
// Navigate to an initial page.
ASSERT_TRUE(NavigateToURL(shell(), initial_url));
FrameTreeNodeId host_id = AddPrerenderWithTags(prerender_url, "tag1");
auto* prerendered_rfh = GetPrerenderedMainFrameHost(host_id);
EXPECT_TRUE(prerendered_rfh != nullptr);
if (IsSpeculationRulesTagsEnabled()) {
EXPECT_TRUE(HasSecSpeculationTagsHeader(prerender_url));
EXPECT_EQ(GetSecSpeculationTagsHeader(prerender_url), "\"tag1\"");
} else {
EXPECT_FALSE(HasSecSpeculationTagsHeader(prerender_url));
}
}
// This prefetch test is tentatively implemented here to reuse the test infra.
// TODO(crbug.com/381687257): Move this test to prefetch browser tests.
IN_PROC_BROWSER_TEST_P(PrerenderRequestHeadersBrowserTest, Prefetch) {

@ -40,6 +40,16 @@ constexpr char kAddSpeculationRuleScript[] = R"({
document.head.appendChild(script);
})";
constexpr char kAddSpeculationRuleWithRulesetTagScript[] = R"({
const script = document.createElement('script');
script.type = 'speculationrules';
script.text = `{
"tag": "$1",
"prerender": [{ $2 }]
}`;
document.head.appendChild(script);
})";
std::string ConvertEagernessToString(
blink::mojom::SpeculationEagerness eagerness) {
switch (eagerness) {
@ -57,7 +67,8 @@ std::string BuildScriptElementSpeculationRules(
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) {
const std::string& target_hint,
std::optional<std::string> ruleset_tag) {
std::stringstream ss;
// Add source filed.
@ -92,8 +103,12 @@ std::string BuildScriptElementSpeculationRules(
ss << base::StringPrintf(R"(, "target_hint": "%s")", target_hint.c_str());
}
return base::ReplaceStringPlaceholders(kAddSpeculationRuleScript, {ss.str()},
nullptr);
return ruleset_tag.has_value()
? base::ReplaceStringPlaceholders(
kAddSpeculationRuleWithRulesetTagScript,
{ruleset_tag.value(), ss.str()}, nullptr)
: base::ReplaceStringPlaceholders(kAddSpeculationRuleScript,
{ss.str()}, nullptr);
}
constexpr char kAddSpeculationRulePrefetchScript[] = R"({
@ -549,7 +564,7 @@ FrameTreeNodeId PrerenderTestHelper::AddPrerender(
int32_t world_id) {
return AddPrerender(prerendering_url, eagerness,
/*no_vary_search_hint=*/std::nullopt, target_hint,
world_id);
/*ruleset_tag=*/std::nullopt, world_id);
}
FrameTreeNodeId PrerenderTestHelper::AddPrerender(
@ -557,6 +572,7 @@ FrameTreeNodeId PrerenderTestHelper::AddPrerender(
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,
int32_t world_id) {
TRACE_EVENT("test", "PrerenderTestHelper::AddPrerender", "prerendering_url",
prerendering_url);
@ -573,14 +589,14 @@ FrameTreeNodeId PrerenderTestHelper::AddPrerender(
run_loop.QuitClosure().Run();
}));
AddPrerendersAsync({prerendering_url}, eagerness, no_vary_search_hint,
target_hint, world_id);
target_hint, ruleset_tag, world_id);
run_loop.Run();
} else {
// For other target hints, the initiator's WebContents will host a
// prerendered page.
prerender_web_contents = GetWebContents();
AddPrerendersAsync({prerendering_url}, eagerness, no_vary_search_hint,
target_hint, world_id);
target_hint, ruleset_tag, world_id);
}
WaitForPrerenderLoadCompletion(*prerender_web_contents, prerendering_url);
@ -602,7 +618,7 @@ void PrerenderTestHelper::AddPrerendersAsync(
int32_t world_id) {
AddPrerendersAsync(prerendering_urls, eagerness,
/*no_vary_search_hint=*/std::nullopt, target_hint,
world_id);
/*ruleset_tag=*/std::nullopt, world_id);
}
void PrerenderTestHelper::AddPrerendersAsync(
@ -610,6 +626,7 @@ void PrerenderTestHelper::AddPrerendersAsync(
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,
int32_t world_id) {
TRACE_EVENT(
"test", "PrerenderTestHelper::AddPrerendersAsync", "prerendering_urls",
@ -621,7 +638,8 @@ void PrerenderTestHelper::AddPrerendersAsync(
"target_hint", target_hint.empty() ? "(empty)" : target_hint);
EXPECT_TRUE(content::BrowserThread::CurrentlyOn(BrowserThread::UI));
std::string script = BuildScriptElementSpeculationRules(
prerendering_urls, eagerness, no_vary_search_hint, target_hint);
prerendering_urls, eagerness, no_vary_search_hint, target_hint,
ruleset_tag);
if (world_id == ISOLATED_WORLD_ID_GLOBAL) {
// Have to use ExecuteJavaScriptForTests instead of ExecJs/EvalJs here,

@ -186,6 +186,7 @@ class PrerenderTestHelper {
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.
@ -201,6 +202,7 @@ class PrerenderTestHelper {
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);