CCT Prefetch: Use PrefetchContainer::referring_origin_ for contamination exemption
Browser-initiated prefetches can be associated with referring origins, but not with referring urls. We can calculate prefetch's contamination exemption from the referring origin, instead of the referring url. Currently CCT prefetch is only the case hitting this, so this change introduces a new delegate that is only limited for CCT case as a quick fix. It is behind the CCT prefetch's flag so that it doesn't affect any other triggers. Later we can clean up this to utilize the referring origin in all triggers. Bug: 404458863, 40288091, 40064203, 40946257 Change-Id: Id8f161fe35af2ca7007f4f59ea71b8a658630a06 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6355551 Reviewed-by: Domenic Denicola <domenic@chromium.org> Reviewed-by: Camille Lamy <clamy@chromium.org> Reviewed-by: Hiroki Nakagawa <nhiroki@chromium.org> Commit-Queue: Taiyo Mizuhashi <taiyo@chromium.org> Reviewed-by: Peter Beverloo <peter@chromium.org> Cr-Commit-Position: refs/heads/main@{#1434163}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
6bbd6edfba
commit
1eaa2dabd5
android_webview/browser/prefetch
chrome/browser/preloading/prefetch/prefetch_service
chrome_prefetch_service_delegate.ccchrome_prefetch_service_delegate.hchrome_prefetch_service_delegate_unittest.cc
content
browser
preloading
public
browser
@ -137,6 +137,14 @@ bool AwPrefetchServiceDelegate::IsContaminationExempt(
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AwPrefetchServiceDelegate::IsContaminationExemptPerOrigin(
|
||||
const url::Origin& referring_origin) {
|
||||
// WebView app initiated prefetching does not use an isolated network context.
|
||||
// However, if WebView ever adds support for non-app triggered prefetching, we
|
||||
// may need to revisit the value returned here.
|
||||
return false;
|
||||
}
|
||||
|
||||
void AwPrefetchServiceDelegate::OnPrefetchLikely(
|
||||
content::WebContents* web_contents) {
|
||||
// Only used for renderer initiated prefetching which WebView doesn't
|
||||
|
@ -41,6 +41,8 @@ class AwPrefetchServiceDelegate : public content::PrefetchServiceDelegate {
|
||||
bool IsExtendedPreloadingEnabled() override;
|
||||
bool IsDomainInPrefetchAllowList(const GURL& referring_url) override;
|
||||
bool IsContaminationExempt(const GURL& referring_url) override;
|
||||
bool IsContaminationExemptPerOrigin(
|
||||
const url::Origin& referring_origin) override;
|
||||
void OnPrefetchLikely(content::WebContents* web_contents) override;
|
||||
void SetAcceptLanguageHeader(std::string accept_language_header) override;
|
||||
|
||||
|
@ -6,6 +6,9 @@
|
||||
|
||||
#include "chrome/browser/battery/battery_saver.h"
|
||||
#include "chrome/browser/data_saver/data_saver.h"
|
||||
#if BUILDFLAG(IS_ANDROID)
|
||||
#include "chrome/browser/flags/android/chrome_feature_list.h"
|
||||
#endif // BUILDFLAG(IS_ANDROID)
|
||||
#include "chrome/browser/preloading/prefetch/prefetch_service/prefetch_origin_decider.h"
|
||||
#include "chrome/browser/preloading/preloading_features.h"
|
||||
#include "chrome/browser/preloading/preloading_prefs.h"
|
||||
@ -118,6 +121,20 @@ bool ChromePrefetchServiceDelegate::IsContaminationExempt(
|
||||
referring_url);
|
||||
}
|
||||
|
||||
bool ChromePrefetchServiceDelegate::IsContaminationExemptPerOrigin(
|
||||
const url::Origin& referring_origin) {
|
||||
#if BUILDFLAG(IS_ANDROID)
|
||||
if (base::FeatureList::IsEnabled(chrome::android::kCCTNavigationalPrefetch)) {
|
||||
TemplateURLService* template_url_service =
|
||||
TemplateURLServiceFactory::GetForProfile(profile_);
|
||||
return template_url_service &&
|
||||
template_url_service->GetDefaultSearchProviderOrigin() ==
|
||||
referring_origin;
|
||||
}
|
||||
#endif // BUILDFLAG(IS_ANDROID)
|
||||
return false;
|
||||
}
|
||||
|
||||
void ChromePrefetchServiceDelegate::OnPrefetchLikely(
|
||||
content::WebContents* web_contents) {
|
||||
page_load_metrics::MetricsWebContentsObserver* metrics_web_contents_observer =
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "base/time/time.h"
|
||||
#include "content/public/browser/prefetch_service_delegate.h"
|
||||
#include "url/gurl.h"
|
||||
#include "url/origin.h"
|
||||
|
||||
namespace content {
|
||||
class BrowserContext;
|
||||
@ -47,6 +48,8 @@ class ChromePrefetchServiceDelegate : public content::PrefetchServiceDelegate {
|
||||
bool IsExtendedPreloadingEnabled() override;
|
||||
bool IsDomainInPrefetchAllowList(const GURL& referring_url) override;
|
||||
bool IsContaminationExempt(const GURL& referring_url) override;
|
||||
bool IsContaminationExemptPerOrigin(
|
||||
const url::Origin& referring_origin) override;
|
||||
void OnPrefetchLikely(content::WebContents* web_contents) override;
|
||||
void SetAcceptLanguageHeader(std::string accept_language_header) override;
|
||||
|
||||
|
@ -4,6 +4,9 @@
|
||||
|
||||
#include "chrome/browser/preloading/prefetch/prefetch_service/chrome_prefetch_service_delegate.h"
|
||||
|
||||
#if BUILDFLAG(IS_ANDROID)
|
||||
#include "chrome/browser/flags/android/chrome_feature_list.h"
|
||||
#endif // BUILDFLAG(IS_ANDROID)
|
||||
#include "chrome/browser/search_engines/template_url_service_factory.h"
|
||||
#include "chrome/browser/search_engines/template_url_service_factory_test_util.h"
|
||||
#include "chrome/test/base/testing_profile.h"
|
||||
@ -26,6 +29,11 @@ class ChromePrefetchServiceDelegateTest : public ::testing::Test {
|
||||
|
||||
TEST_F(ChromePrefetchServiceDelegateTest,
|
||||
DefaultSearchEngineIsContaminationExempt) {
|
||||
#if BUILDFLAG(IS_ANDROID)
|
||||
base::test::ScopedFeatureList scoped_feature_list;
|
||||
scoped_feature_list.InitWithFeatures(
|
||||
{chrome::android::kCCTNavigationalPrefetch}, {});
|
||||
#endif // BUILDFLAG(IS_ANDROID)
|
||||
TemplateURLData data;
|
||||
data.SetShortName(u"Sherlock");
|
||||
data.SetKeyword(u"sherlock");
|
||||
@ -39,4 +47,10 @@ TEST_F(ChromePrefetchServiceDelegateTest,
|
||||
GURL("https://sherlock.example/?q=professor+moriarty")));
|
||||
EXPECT_FALSE(
|
||||
delegate.IsContaminationExempt(GURL("https://another.example/")));
|
||||
#if BUILDFLAG(IS_ANDROID)
|
||||
EXPECT_TRUE(delegate.IsContaminationExemptPerOrigin(
|
||||
url::Origin::Create(GURL("https://sherlock.example/"))));
|
||||
EXPECT_FALSE(delegate.IsContaminationExemptPerOrigin(
|
||||
url::Origin::Create(GURL("https://another.example/"))));
|
||||
#endif // BUILDFLAG(IS_ANDROID)
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "content/public/test/browser_test_utils.h"
|
||||
#include "content/public/test/content_browser_test.h"
|
||||
#include "content/public/test/content_browser_test_utils.h"
|
||||
#include "content/public/test/prefetch_test_util.h"
|
||||
#include "content/shell/browser/shell.h"
|
||||
#include "net/http/http_status_code.h"
|
||||
#include "net/test/embedded_test_server/embedded_test_server.h"
|
||||
@ -219,6 +220,48 @@ IN_PROC_BROWSER_TEST_F(ContaminationDelayBrowserTest, IgnoresIfExempt) {
|
||||
BrowsingContextGroupSwapType::kProactiveSwap));
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(ContaminationDelayBrowserTest,
|
||||
IgnoresIfExempt_BrowserInitiated) {
|
||||
url::Origin referring_origin = url::Origin::Create(
|
||||
embedded_test_server()->GetURL("referrer.localhost", "/title1.html"));
|
||||
GURL prefetch_url =
|
||||
embedded_test_server()->GetURL("prefetch.localhost", "/delayed");
|
||||
|
||||
auto* prefetch_service = PrefetchService::GetFromFrameTreeNodeId(
|
||||
shell()->web_contents()->GetPrimaryMainFrame()->GetFrameTreeNodeId());
|
||||
// TODO(crbug.com/40946257): Currently `OnPrefetchLikely` will never be called
|
||||
// for browser-initiated triggers, so we set `num_on_prefetch_likely_calls` to
|
||||
// 0 here, and instead use `TestPrefetchWatcher` to confirm whether prefetch
|
||||
// is actually triggered.
|
||||
auto owned_delegate = std::make_unique<MockPrefetchServiceDelegate>(
|
||||
/*num_on_prefetch_likely_calls=*/0);
|
||||
EXPECT_CALL(*owned_delegate, IsContaminationExemptPerOrigin(referring_origin))
|
||||
.WillRepeatedly(testing::Return(true));
|
||||
prefetch_service->SetPrefetchServiceDelegateForTesting(
|
||||
std::move(owned_delegate));
|
||||
|
||||
auto test_prefetch_watcher = std::make_unique<test::TestPrefetchWatcher>();
|
||||
auto handle = shell()->web_contents()->StartPrefetch(
|
||||
prefetch_url, /*use_prefetch_proxy=*/false, blink::mojom::Referrer(),
|
||||
referring_origin, /*no_vary_search_hint=*/std::nullopt,
|
||||
content::PreloadPipelineInfo::Create(
|
||||
/*planned_max_preloading_type=*/content::PreloadingType::kPrefetch),
|
||||
/*attempt=*/nullptr, /*holdback_status_override=*/std::nullopt);
|
||||
test_prefetch_watcher->WaitUntilPrefetchResponseCompleted(std::nullopt,
|
||||
prefetch_url);
|
||||
|
||||
BrowsingContextGroupSwapObserver swap_observer(shell()->web_contents());
|
||||
base::ElapsedTimer timer;
|
||||
ASSERT_TRUE(NavigateToURL(shell(), prefetch_url));
|
||||
EXPECT_TRUE(test_prefetch_watcher->PrefetchUsedInLastNavigation());
|
||||
EXPECT_LT(timer.Elapsed(), response_delay());
|
||||
EXPECT_THAT(swap_observer.Get().type(),
|
||||
::testing::AnyOf(BrowsingContextGroupSwapType::kNoSwap,
|
||||
BrowsingContextGroupSwapType::kProactiveSwap));
|
||||
test_prefetch_watcher.reset();
|
||||
handle.reset();
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(ContaminationDelayBrowserTest, DelayAfterRedirect) {
|
||||
set_response_delay(TestTimeouts::tiny_timeout() * 8);
|
||||
|
||||
|
@ -31,6 +31,8 @@ MockPrefetchServiceDelegate::MockPrefetchServiceDelegate(
|
||||
ON_CALL(*this, IsDataSaverEnabled).WillByDefault(testing::Return(false));
|
||||
ON_CALL(*this, IsBatterySaverEnabled).WillByDefault(testing::Return(false));
|
||||
ON_CALL(*this, IsContaminationExempt).WillByDefault(testing::Return(false));
|
||||
ON_CALL(*this, IsContaminationExemptPerOrigin)
|
||||
.WillByDefault(testing::Return(false));
|
||||
ON_CALL(*this, IsDomainInPrefetchAllowList(testing::_))
|
||||
.WillByDefault(testing::Return(true));
|
||||
|
||||
|
@ -45,6 +45,10 @@ class MockPrefetchServiceDelegate : public PrefetchServiceDelegate {
|
||||
MOCK_METHOD(bool, IsBatterySaverEnabled, (), (override));
|
||||
MOCK_METHOD(bool, IsDomainInPrefetchAllowList, (const GURL&), (override));
|
||||
MOCK_METHOD(bool, IsContaminationExempt, (const GURL&), (override));
|
||||
MOCK_METHOD(bool,
|
||||
IsContaminationExemptPerOrigin,
|
||||
(const url::Origin&),
|
||||
(override));
|
||||
MOCK_METHOD(void, OnPrefetchLikely, (WebContents*), (override));
|
||||
MOCK_METHOD(void, SetAcceptLanguageHeader, (std::string), (override));
|
||||
};
|
||||
|
@ -969,14 +969,26 @@ void PrefetchService::OnGotCookiesForEligibilityCheck(
|
||||
// The cookie eligibility check just happened, and we might proceed anyway.
|
||||
// We might therefore need to delay further processing to the extent
|
||||
// required to obscure the outcome of this check from the current site.
|
||||
// TODO(crbug.com/40946257): Currently browser-initiated prefetch will
|
||||
// always be marked as cross-site contaminated since there is no initiator
|
||||
// rfh. Revisit and add proper handlings.
|
||||
auto* initiator_rfh = RenderFrameHost::FromID(
|
||||
prefetch_container->GetReferringRenderFrameHostId());
|
||||
const bool is_contamination_exempt =
|
||||
delegate_ && initiator_rfh &&
|
||||
delegate_->IsContaminationExempt(initiator_rfh->GetLastCommittedURL());
|
||||
const bool is_contamination_exempt = [&] {
|
||||
if (!prefetch_container->IsRendererInitiated()) {
|
||||
// When browser-initiated prefetches, we can calculates prefetch's
|
||||
// contamination exemption from the referring origin. Currently CCT
|
||||
// prefetch is only the case hitting this, so the callee will check
|
||||
// whether it is behind the feature flag tentatively.
|
||||
// TODO(crbug.com/40946257): Migrate to use this in all cases.
|
||||
return delegate_ &&
|
||||
prefetch_container->GetReferringOrigin().has_value() &&
|
||||
delegate_->IsContaminationExemptPerOrigin(
|
||||
prefetch_container->GetReferringOrigin().value());
|
||||
} else {
|
||||
return delegate_ && initiator_rfh &&
|
||||
delegate_->IsContaminationExempt(
|
||||
initiator_rfh->GetLastCommittedURL());
|
||||
}
|
||||
}();
|
||||
|
||||
if (!is_contamination_exempt) {
|
||||
prefetch_container->MarkCrossSiteContaminated();
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "content/common/content_export.h"
|
||||
#include "content/public/browser/preloading.h"
|
||||
#include "url/gurl.h"
|
||||
#include "url/origin.h"
|
||||
|
||||
namespace content {
|
||||
|
||||
@ -66,9 +67,11 @@ class CONTENT_EXPORT PrefetchServiceDelegate {
|
||||
// Checks if the referring page is in the allow list to make prefetches.
|
||||
virtual bool IsDomainInPrefetchAllowList(const GURL& referring_url) = 0;
|
||||
|
||||
// Determines whether a referring URL is reasonably trusted to proceed without
|
||||
// delay when processing cross-site prefetches.
|
||||
// Determines whether a referring URL or origin is reasonably trusted to
|
||||
// proceed without delay when processing cross-site prefetches.
|
||||
virtual bool IsContaminationExempt(const GURL& referring_url) = 0;
|
||||
virtual bool IsContaminationExemptPerOrigin(
|
||||
const url::Origin& referring_origin) = 0;
|
||||
|
||||
virtual void OnPrefetchLikely(WebContents* web_contents) = 0;
|
||||
|
||||
|
Reference in New Issue
Block a user