0

Extend PrefetchContainer::referring_origin_ for general browser-initiated prefetch

This CL is based on wbjacksonjr@'s proposal.

Currently, all prefetch triggers have a concept of referring
document/origin. Specifically, potential value of PrefetchContainer::referring_origin_ is
- If CCT (prefetch from App A that has verified associated origin):
  referring_origin == A’s origin
- If CCT (otherwise): referring_origin == opaque origin [1]
  so that we can treat this request as a cross-site prefetch. Also, if
  nullopt is passed to PrefetchContainer's public ctor for browser-
  initiated prefetch, it is automatically converted to opaque origin [2]
- If Speculation rules prefetch in document A: referring_origin == A’s
  origin

In order to introduce WebView prefetch, which has no concepts that
corresponds to referring document/origin and thus has no restriction
related to it, this CL changes the type of
PrefetchContainer::referring_origin_ to std::optional so that general
browser-initiated prefetch can use this.
Practically this case can be handled like same-site prefetch, in the
same manner as normal browser-initiated prerender behaves like
same-site prerender.

[1] https://source.chromium.org/chromium/chromium/src/+/main:chrome/android/java/src/org/chromium/chrome/browser/WarmupManager.java;l=663;drc=8f3124aee72eb5cc75bec53fd9e7999592767e9a
[2] https://source.chromium.org/chromium/chromium/src/+/main:content/browser/preloading/prefetch/prefetch_container.cc;l=426;drc=4d3dad034f8aefd3a1c6043eecbd901568546936

Bug: 40946257, 363946909
Co-authored-by: Wayne Jackson Jr. <wbjacksonjr@chromium.org>
Change-Id: Ic0d1bf1e3350c719020a6a9b8232a50acc170ab4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5942243
Reviewed-by: Wayne Jackson Jr. <wbjacksonjr@chromium.org>
Reviewed-by: Kouhei Ueno <kouhei@chromium.org>
Commit-Queue: Kouhei Ueno <kouhei@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1373194}
This commit is contained in:
Taiyo Mizuhashi
2024-10-24 08:44:20 +00:00
committed by Chromium LUCI CQ
parent d71d00babd
commit 9dfeb633bc
7 changed files with 54 additions and 36 deletions

@ -336,7 +336,7 @@ bool CalculateIsLikelyAheadOfPrerender(PreloadingAttempt* attempt) {
class PrefetchContainer::SinglePrefetch {
public:
explicit SinglePrefetch(const GURL& url,
const url::Origin& referring_origin,
bool is_isolated_network_context_required,
bool is_reusable);
~SinglePrefetch();
@ -423,7 +423,7 @@ PrefetchContainer::PrefetchContainer(
std::optional<PreloadingHoldbackStatus> holdback_status_override)
: PrefetchContainer(
GlobalRenderFrameHostId(),
referring_origin.value_or(url::Origin()),
referring_origin,
/*referring_url_hash=*/std::nullopt,
PrefetchContainer::Key(
std::optional<blink::DocumentToken>(std::nullopt),
@ -456,7 +456,7 @@ PrefetchContainer::PrefetchContainer(
base::WeakPtr<PreloadingAttempt> attempt,
std::optional<PrefetchStartCallback> prefetch_start_callback)
: PrefetchContainer(GlobalRenderFrameHostId(),
referring_origin.value_or(url::Origin()),
referring_origin,
/*referring_url_hash=*/std::nullopt,
PrefetchContainer::Key(
std::optional<blink::DocumentToken>(std::nullopt),
@ -479,7 +479,7 @@ PrefetchContainer::PrefetchContainer(
PrefetchContainer::PrefetchContainer(
const GlobalRenderFrameHostId& referring_render_frame_host_id,
const url::Origin& referring_origin,
const std::optional<url::Origin>& referring_origin,
const std::optional<size_t>& referring_url_hash,
const PrefetchContainer::Key& key,
const PrefetchType& prefetch_type,
@ -551,7 +551,8 @@ PrefetchContainer::PrefetchContainer(
return false;
}();
redirect_chain_.push_back(std::make_unique<SinglePrefetch>(
GetURL(), referring_origin_, is_reusable));
GetURL(), IsCrossSiteRequest(url::Origin::Create(GetURL())),
is_reusable));
}
PrefetchContainer::~PrefetchContainer() {
@ -1010,12 +1011,24 @@ void PrefetchContainer::AddRedirectHop(const net::RedirectInfo& redirect_info) {
AddXClientDataHeader(*resource_request_.get());
redirect_chain_.push_back(std::make_unique<SinglePrefetch>(
redirect_info.new_url, referring_origin_,
redirect_info.new_url,
IsCrossSiteRequest(url::Origin::Create(redirect_info.new_url)),
// If `PrefetchResponseReader` of the initial navigation is reusable,
// inherit the property.
redirect_chain_[0]->response_reader_->is_reusable()));
}
bool PrefetchContainer::IsCrossSiteRequest(const url::Origin& origin) const {
return referring_origin_.has_value() &&
net::SchemefulSite(referring_origin_.value()) !=
net::SchemefulSite(origin);
}
bool PrefetchContainer::IsCrossOriginRequest(const url::Origin& origin) const {
return referring_origin_.has_value() &&
!referring_origin_.value().IsSameOriginWith(origin);
}
void PrefetchContainer::MarkCrossSiteContaminated() {
is_cross_site_contaminated_ = true;
}
@ -1728,7 +1741,7 @@ PrefetchStatus PrefetchContainer::Reader::GetPrefetchStatus() const {
}
bool PrefetchContainer::IsProxyRequiredForURL(const GURL& url) const {
return !referring_origin_.IsSameOriginWith(url) &&
return IsCrossOriginRequest(url::Origin::Create(url)) &&
prefetch_type_.IsProxyRequiredWhenCrossOrigin();
}
@ -1856,11 +1869,10 @@ void PrefetchContainer::AddClientHintsHeaders(
// prefetch, and potentially a cross-site only. (This logic might need to be
// revisited if we ever supported prefetching in another site's partition,
// such as in a subframe.)
const bool is_same_site =
net::SchemefulSite(referring_origin_) == net::SchemefulSite(origin);
const bool is_cross_site = IsCrossSiteRequest(origin);
const auto cross_site_behavior =
features::kPrefetchClientHintsCrossSiteBehavior.Get();
if (is_same_site ||
if (!is_cross_site ||
cross_site_behavior ==
features::PrefetchClientHintsCrossSiteBehavior::kAll) {
request_headers->MergeFrom(client_hints_headers);
@ -1933,11 +1945,11 @@ CONTENT_EXPORT std::ostream& operator<<(
PrefetchContainer::SinglePrefetch::SinglePrefetch(
const GURL& url,
const url::Origin& referring_origin,
bool is_isolated_network_context_required,
bool is_reusable)
: url_(url),
is_isolated_network_context_required_(
net::SchemefulSite(referring_origin) != net::SchemefulSite(url_)),
is_isolated_network_context_required),
response_reader_(
base::MakeRefCounted<PrefetchResponseReader>(is_reusable)) {}

@ -111,8 +111,7 @@ class CONTENT_EXPORT PrefetchContainer {
// Ctor used for browser-initiated prefetch.
// We can pass the referring origin of prefetches via `referring_origin` if
// necessary. When `std::nullopt` is passed, the referring origin will be
// opaque.
// necessary.
PrefetchContainer(
WebContents& referring_web_contents,
const GURL& url,
@ -126,8 +125,7 @@ class CONTENT_EXPORT PrefetchContainer {
// Ctor used for browser-initiated prefetch that doesn't depend on web
// contents. We can pass the referring origin of prefetches via
// `referrer_origin` if necessary. When `std::nullopt` is passed, the
// referring origin will be opaque.
// `referrer_origin` if necessary.
PrefetchContainer(
BrowserContext* browser_context,
const GURL& url,
@ -268,7 +266,9 @@ class CONTENT_EXPORT PrefetchContainer {
bool IsRendererInitiated() const;
// The origin and that initiates the prefetch request.
const url::Origin& GetReferringOrigin() const { return referring_origin_; }
const std::optional<url::Origin> GetReferringOrigin() const {
return referring_origin_;
}
// Whether or not an isolated network context is required to the next
// prefetch.
@ -384,6 +384,10 @@ class CONTENT_EXPORT PrefetchContainer {
void SetIsDecoy(bool is_decoy) { is_decoy_ = is_decoy; }
bool IsDecoy() const { return is_decoy_; }
// Whether the prefetch request is cross-site/cross-origin for given origin.
bool IsCrossSiteRequest(const url::Origin& origin) const;
bool IsCrossOriginRequest(const url::Origin& origin) const;
// Whether this prefetch is potentially contaminated by cross-site state.
// If so, it may need special handling for privacy.
// See https://crbug.com/1439246.
@ -769,7 +773,7 @@ class CONTENT_EXPORT PrefetchContainer {
private:
PrefetchContainer(
const GlobalRenderFrameHostId& referring_render_frame_host_id,
const url::Origin& referring_origin,
const std::optional<url::Origin>& referring_origin,
const std::optional<size_t>& referring_url_hash,
const PrefetchContainer::Key& key,
const PrefetchType& prefetch_type,
@ -837,8 +841,8 @@ class CONTENT_EXPORT PrefetchContainer {
// The origin and URL that initiates the prefetch request.
// For renderer-initiated prefetch, this is calculated by referring
// RenderFrameHost's LastCommittedOrigin. For browser-initiated prefetch, this
// is sometimes explicitly passed via ctor, otherwise opaque origin.
const url::Origin referring_origin_;
// is sometimes explicitly passed via ctor.
const std::optional<url::Origin> referring_origin_;
// Used by metrics for equality checks, only works for renderer-initiated
// triggers.
const std::optional<size_t> referring_url_hash_;

@ -312,7 +312,7 @@ TEST_P(PrefetchContainerTest, CreatePrefetchContainer_Embedder) {
PrefetchContainer prefetch_container(
*web_contents(), GURL("https://test.com"),
PrefetchType(PreloadingTriggerType::kEmbedder,
/*use_prefetch_proxy=*/true),
/*use_prefetch_proxy=*/false),
blink::mojom::Referrer(), /*referring_origin=*/std::nullopt,
/*no_vary_search_expected=*/std::nullopt, /*attempt=*/nullptr);
@ -321,8 +321,8 @@ TEST_P(PrefetchContainerTest, CreatePrefetchContainer_Embedder) {
EXPECT_EQ(prefetch_container.GetURL(), GURL("https://test.com"));
EXPECT_EQ(prefetch_container.GetPrefetchType(),
PrefetchType(PreloadingTriggerType::kEmbedder,
/*use_prefetch_proxy=*/true));
EXPECT_TRUE(
/*use_prefetch_proxy=*/false));
EXPECT_FALSE(
prefetch_container.IsIsolatedNetworkContextRequiredForCurrentPrefetch());
EXPECT_EQ(prefetch_container.key(),
@ -1064,8 +1064,8 @@ TEST_P(PrefetchContainerTest, IsIsolatedNetworkRequired_Embedder) {
auto prefetch_container_default = CreateEmbedderPrefetchContainer(
GURL("https://test.com/prefetch"), std::nullopt);
prefetch_container_default->MakeResourceRequest({});
EXPECT_TRUE(prefetch_container_default
->IsIsolatedNetworkContextRequiredForCurrentPrefetch());
EXPECT_FALSE(prefetch_container_default
->IsIsolatedNetworkContextRequiredForCurrentPrefetch());
auto prefetch_container_same_origin = CreateEmbedderPrefetchContainer(
GURL("https://test.com/prefetch"),

@ -36,7 +36,7 @@ PrefetchNetworkContext::PrefetchNetworkContext(
bool use_isolated_network_context,
const PrefetchType& prefetch_type,
const GlobalRenderFrameHostId& referring_render_frame_host_id,
const url::Origin& referring_origin)
const std::optional<url::Origin>& referring_origin)
: use_isolated_network_context_(use_isolated_network_context),
prefetch_type_(prefetch_type),
referring_render_frame_host_id_(referring_render_frame_host_id),
@ -209,7 +209,8 @@ PrefetchNetworkContext::CreateNewURLLoaderFactory(
url_loader_factory::HeaderClientOption::kAllow),
url_loader_factory::ContentClientParams(
browser_context, referring_render_frame_host,
referring_render_process_id, referring_origin_, net::IsolationInfo(),
referring_render_process_id,
referring_origin_.value_or(url::Origin()), net::IsolationInfo(),
ukm_source_id, &bypass_redirect_checks));
}

@ -31,7 +31,7 @@ class CONTENT_EXPORT PrefetchNetworkContext {
bool use_isolated_network_context,
const PrefetchType& prefetch_type,
const GlobalRenderFrameHostId& referring_render_frame_host_id,
const url::Origin& referring_origin);
const std::optional<url::Origin>& referring_origin);
~PrefetchNetworkContext();
PrefetchNetworkContext(const PrefetchNetworkContext&) = delete;
@ -78,7 +78,7 @@ class CONTENT_EXPORT PrefetchNetworkContext {
// proxy |url_loader_factory_| by calling WillCreateURLLoaderFactory.
// For renderer-initiated prefetch, this is calculated by referring
// RenderFrameHost's LastCommittedOrigin.
const url::Origin referring_origin_;
const std::optional<url::Origin> referring_origin_;
// The network context and URL loader factory to use when making prefetches.
mojo::Remote<network::mojom::NetworkContext> network_context_;

@ -523,8 +523,9 @@ void PrefetchService::PrefetchUrl(
(PrefetchAllowAllDomainsForExtendedPreloading() &&
delegate_->IsExtendedPreloadingEnabled());
if (!allow_all_domains &&
prefetch_container->GetReferringOrigin().has_value() &&
!delegate_->IsDomainInPrefetchAllowList(
prefetch_container->GetReferringOrigin().GetURL())) {
prefetch_container->GetReferringOrigin().value().GetURL())) {
DVLOG(1) << *prefetch_container
<< ": not prefetched (not in allow list)";
return;

@ -1370,13 +1370,13 @@ TEST_P(PrefetchServiceTest, SuccessCase_Embedder) {
MakePrefetchFromEmbedder(GURL("https://example.com"),
PrefetchType(PreloadingTriggerType::kEmbedder,
/*use_prefetch_proxy=*/true));
/*use_prefetch_proxy=*/false));
task_environment()->RunUntilIdle();
VerifyCommonRequestState(GURL("https://example.com"),
{.use_prefetch_proxy = true});
{.use_prefetch_proxy = false});
MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
/*use_prefetch_proxy=*/true,
/*use_prefetch_proxy=*/false,
{{"X-Testing", "Hello World"}}, kHTMLBody);
// Verify that the prefetch request was successful.
@ -1416,13 +1416,13 @@ TEST_P(PrefetchServiceTest,
MakePrefetchFromEmbedder(GURL("https://example.com"),
PrefetchType(PreloadingTriggerType::kEmbedder,
/*use_prefetch_proxy=*/true));
/*use_prefetch_proxy=*/false));
task_environment()->RunUntilIdle();
VerifyCommonRequestState(GURL("https://example.com"),
{.use_prefetch_proxy = true});
{.use_prefetch_proxy = false});
MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
/*use_prefetch_proxy=*/true,
/*use_prefetch_proxy=*/false,
{{"X-Testing", "Hello World"}}, kHTMLBody);
// Verify that the prefetch request was successful.