0

Revert "Topics: Sending Topics with image requests"

This reverts commit 072152ec38.

Reason for revert: See https://issues.chromium.org/u/1/issues/387306732

Original change's description:
> Topics: Sending Topics with image requests
>
> Send the Topics request header on image requests that specify the "browsingTopics" attribute.
>
> Use much of the existing plumbing work from the fetch() API. Adapt many
> of the tests for the fetch() API to also run for <img> attribute.
>
> PR: https://github.com/patcg-individual-drafts/topics/pull/357
>
> Bug: 382532794
>
> Change-Id: Id5975006a0c842df8765c49470c5f30bca7586c0
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6067268
> Commit-Queue: Abigail Katcoff <abigailkatcoff@chromium.org>
> Reviewed-by: Chris Harrelson <chrishtr@chromium.org>
> Reviewed-by: Robert Ogden <robertogden@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#1401483}

Bug: 382532794
Change-Id: Ia0403c388a639a0ff0a77dce9497108f2056aefd
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6138635
Owners-Override: Tim Sergeant <tsergeant@chromium.org>
Commit-Queue: David Yeung <dayeung@chromium.org>
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Reviewed-by: Tim Sergeant <tsergeant@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1401650}
This commit is contained in:
David Yeung
2025-01-02 16:58:52 -08:00
committed by Chromium LUCI CQ
parent 679cb25d08
commit 4945b31056
26 changed files with 243 additions and 536 deletions

@ -3524,7 +3524,6 @@ interface HTMLImageElement : HTMLElement
getter alt
getter attributionSrc
getter border
getter browsingTopics
getter complete
getter crossOrigin
getter currentSrc
@ -3554,7 +3553,6 @@ interface HTMLImageElement : HTMLElement
setter alt
setter attributionSrc
setter border
setter browsingTopics
setter crossOrigin
setter decoding
setter fetchPriority
@ -4616,7 +4614,6 @@ interface Image
getter alt
getter attributionSrc
getter border
getter browsingTopics
getter complete
getter crossOrigin
getter currentSrc
@ -4646,7 +4643,6 @@ interface Image
setter alt
setter attributionSrc
setter border
setter browsingTopics
setter crossOrigin
setter decoding
setter fetchPriority

@ -3522,7 +3522,6 @@ interface HTMLImageElement : HTMLElement
getter alt
getter attributionSrc
getter border
getter browsingTopics
getter complete
getter crossOrigin
getter currentSrc
@ -3552,7 +3551,6 @@ interface HTMLImageElement : HTMLElement
setter alt
setter attributionSrc
setter border
setter browsingTopics
setter crossOrigin
setter decoding
setter fetchPriority
@ -4614,7 +4612,6 @@ interface Image
getter alt
getter attributionSrc
getter border
getter browsingTopics
getter complete
getter crossOrigin
getter currentSrc
@ -4644,7 +4641,6 @@ interface Image
setter alt
setter attributionSrc
setter border
setter browsingTopics
setter crossOrigin
setter decoding
setter fetchPriority

@ -21,6 +21,7 @@
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/mixin_based_in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/browsing_topics/browsing_topics_service.h"
#include "components/browsing_topics/browsing_topics_service_impl.h"
#include "components/browsing_topics/epoch_topics.h"
#include "components/browsing_topics/test_util.h"
@ -622,105 +623,6 @@ class BrowsingTopicsBrowserTest : public BrowsingTopicsBrowserTestBase {
base::CallbackListSubscription subscription_;
};
class BrowsingTopicsSubresourceRequestTest
: public BrowsingTopicsBrowserTest,
public ::testing::WithParamInterface<bool> {
public:
// If true, test the fetch option. If false, test the img attribute.
bool TestFetch() { return GetParam(); }
std::string GetRelativePath() {
if (TestFetch()) {
return "/browsing_topics/page_with_custom_topics_header.html";
}
return "/browsing_topics/topics-writable-pixel.png";
}
std::string GetRedirectRelativePath() {
if (TestFetch()) {
return "/browsing_topics/page_with_custom_topics_header2.html";
}
return "/browsing_topics/topics-writable-pixel2.png";
}
bool ExecJsWithBrowsingTopicsTrue(GURL url) {
if (TestFetch()) {
return ExecJs(
web_contents()->GetPrimaryMainFrame(),
content::JsReplace("fetch($1, {browsingTopics: true})", url));
}
return ExecJs(web_contents()->GetPrimaryMainFrame(),
content::JsReplace(R"(
let img = document.createElement('img');
img.src = $1;
img.browsingTopics = true;
img.decode()
.then(() => {
document.body.appendChild(img);
})
)",
url.spec()));
}
bool ExecJSWithBrowsingTopicsFalse(GURL url) {
if (TestFetch()) {
return ExecJs(
web_contents()->GetPrimaryMainFrame(),
content::JsReplace("fetch($1, {browsingTopics: false})", url));
}
return ExecJs(web_contents()->GetPrimaryMainFrame(),
content::JsReplace(R"(
let img = document.createElement('img');
img.src = $1;
img.browsingTopics = false;
img.decode()
.then(() => {
document.body.appendChild(img);
})
)",
url.spec()));
}
bool ExecJsWithMissingBrowsingTopicsAttribute(GURL url) {
if (TestFetch()) {
return ExecJs(web_contents()->GetPrimaryMainFrame(),
content::JsReplace("fetch($1)", url));
}
return ExecJs(web_contents()->GetPrimaryMainFrame(),
content::JsReplace(R"(
let img = document.createElement('img');
img.src = $1;
img.decode()
.then(() => {
document.body.appendChild(img);
})
)",
url.spec()));
}
int GetBrowsingTopicsApiActionType(bool observe) {
if (TestFetch()) {
if (observe) {
return 3; // kObserveViaFetchLikeApi
}
return 2; // kGetViaFetchLikeApi;
}
if (observe) {
return 7; // kObserveViaImgAttributeApi
}
return 6; // kGetViaImgAttributeApi
}
};
INSTANTIATE_TEST_SUITE_P(All,
BrowsingTopicsSubresourceRequestTest,
::testing::Bool());
IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest, HasBrowsingTopicsService) {
EXPECT_TRUE(browsing_topics_service());
}
@ -1261,19 +1163,24 @@ IN_PROC_BROWSER_TEST_F(
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), new_url));
}
IN_PROC_BROWSER_TEST_P(
BrowsingTopicsSubresourceRequestTest,
SameOrigin_TopicsEligible_SendTopics_HasNoObserveResponse) {
IN_PROC_BROWSER_TEST_F(
BrowsingTopicsBrowserTest,
FetchSameOrigin_TopicsEligible_SendTopics_HasNoObserveResponse) {
GURL main_frame_url =
https_server_.GetURL("a.test", "/browsing_topics/empty_page.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_frame_url));
GURL resource_url = https_server_.GetURL("a.test", GetRelativePath());
GURL fetch_url = https_server_.GetURL(
"a.test", "/browsing_topics/page_with_custom_topics_header.html");
EXPECT_TRUE(ExecJsWithBrowsingTopicsTrue(resource_url));
EXPECT_TRUE(ExecJs(
web_contents()->GetPrimaryMainFrame(),
content::JsReplace("fetch($1, {browsingTopics: true})", fetch_url)));
std::optional<std::string> topics_header_value =
GetTopicsHeaderForRequestPath(GetRelativePath());
GetTopicsHeaderForRequestPath(
"/browsing_topics/page_with_custom_topics_header.html");
EXPECT_TRUE(topics_header_value);
EXPECT_EQ(*topics_header_value, kExpectedHeaderValueForSiteA);
@ -1284,22 +1191,23 @@ IN_PROC_BROWSER_TEST_P(
EXPECT_EQ(api_usage_contexts.size(), 1u);
}
IN_PROC_BROWSER_TEST_P(BrowsingTopicsSubresourceRequestTest,
WithoutTopicsFlagSet) {
IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest, FetchWithoutTopicsFlagSet) {
GURL main_frame_url =
https_server_.GetURL("b.test", "/browsing_topics/empty_page.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_frame_url));
GURL resource_url = https_server_.GetURL("b.test", GetRelativePath());
GURL fetch_url = https_server_.GetURL(
"b.test", "/browsing_topics/page_with_custom_topics_header.html");
{
// Invoke fetch() or img without the `browsingTopics` flag. This request
// isn't eligible for topics.
EXPECT_TRUE(ExecJsWithMissingBrowsingTopicsAttribute(resource_url));
// Invoke fetch() without the `browsingTopics` flag. This request isn't
// eligible for topics.
EXPECT_TRUE(ExecJs(web_contents()->GetPrimaryMainFrame(),
content::JsReplace("fetch($1)", fetch_url)));
std::optional<std::string> topics_header_value =
GetTopicsHeaderForRequestPath(GetRelativePath());
GetTopicsHeaderForRequestPath(
"/browsing_topics/page_with_custom_topics_header.html");
// Expect no topics header as the request did not specify
// {browsingTopics: true}.
@ -1307,12 +1215,15 @@ IN_PROC_BROWSER_TEST_P(BrowsingTopicsSubresourceRequestTest,
}
{
// Invoke fetch() or img with the `browsingTopics` flag set to false. This
// request isn't eligible for topics.
EXPECT_TRUE(ExecJSWithBrowsingTopicsFalse(resource_url));
// Invoke fetch() with the `browsingTopics` flag set to false. This request
// isn't eligible for topics.
EXPECT_TRUE(ExecJs(
web_contents()->GetPrimaryMainFrame(),
content::JsReplace("fetch($1, {browsingTopics: false})", fetch_url)));
std::optional<std::string> topics_header_value =
GetTopicsHeaderForRequestPath(GetRelativePath());
GetTopicsHeaderForRequestPath(
"/browsing_topics/page_with_custom_topics_header.html");
// Expect no topics header as the request did not specify
// {browsingTopics: true}.
@ -1320,21 +1231,25 @@ IN_PROC_BROWSER_TEST_P(BrowsingTopicsSubresourceRequestTest,
}
}
IN_PROC_BROWSER_TEST_P(
BrowsingTopicsSubresourceRequestTest,
SameOrigin_TopicsEligible_SendNoTopic_HasNoObserveResponse) {
IN_PROC_BROWSER_TEST_F(
BrowsingTopicsBrowserTest,
FetchSameOrigin_TopicsEligible_SendNoTopic_HasNoObserveResponse) {
base::HistogramTester histogram_tester;
GURL main_frame_url =
https_server_.GetURL("b.test", "/browsing_topics/empty_page.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_frame_url));
GURL resource_url = https_server_.GetURL("b.test", GetRelativePath());
GURL fetch_url = https_server_.GetURL(
"b.test", "/browsing_topics/page_with_custom_topics_header.html");
EXPECT_TRUE(ExecJsWithBrowsingTopicsTrue(resource_url));
EXPECT_TRUE(ExecJs(
web_contents()->GetPrimaryMainFrame(),
content::JsReplace("fetch($1, {browsingTopics: true})", fetch_url)));
std::optional<std::string> topics_header_value =
GetTopicsHeaderForRequestPath(GetRelativePath());
GetTopicsHeaderForRequestPath(
"/browsing_topics/page_with_custom_topics_header.html");
// Expect an empty header value as "b.test" did not observe the candidate
// topics.
@ -1347,15 +1262,14 @@ IN_PROC_BROWSER_TEST_P(
content::GetBrowsingTopicsApiUsage(browsing_topics_site_data_manager());
EXPECT_EQ(api_usage_contexts.size(), 1u);
histogram_tester.ExpectUniqueSample(
kBrowsingTopicsApiActionTypeHistogramId,
GetBrowsingTopicsApiActionType(/*observe=*/false),
/*expected_bucket_count=*/1);
histogram_tester.ExpectUniqueSample(kBrowsingTopicsApiActionTypeHistogramId,
2 /*kGetViaFetchLikeApi*/,
/*expected_bucket_count=*/1);
}
IN_PROC_BROWSER_TEST_P(
BrowsingTopicsSubresourceRequestTest,
SameOrigin_TopicsEligible_SendNoTopic_HasObserveResponse) {
IN_PROC_BROWSER_TEST_F(
BrowsingTopicsBrowserTest,
FetchSameOrigin_TopicsEligible_SendNoTopic_HasObserveResponse) {
base::HistogramTester histogram_tester;
GURL main_frame_url =
@ -1368,11 +1282,15 @@ IN_PROC_BROWSER_TEST_P(
"Observe-Browsing-Topics: ?1"));
replacement.emplace_back(std::make_pair("{{REDIRECT_HEADER}}", ""));
GURL resource_url = https_server_.GetURL(
"b.test", net::test_server::GetFilePathWithReplacements(GetRelativePath(),
replacement));
GURL fetch_url = https_server_.GetURL(
"b.test", net::test_server::GetFilePathWithReplacements(
"/browsing_topics/"
"page_with_custom_topics_header.html",
replacement));
EXPECT_TRUE(ExecJsWithBrowsingTopicsTrue(resource_url));
EXPECT_TRUE(ExecJs(
web_contents()->GetPrimaryMainFrame(),
content::JsReplace("fetch($1, {browsingTopics: true})", fetch_url)));
// A new observation should have been recorded in addition to the pre-existing
// one, as the response had the `Observe-Browsing-Topics: ?1` header and the
@ -1392,19 +1310,17 @@ IN_PROC_BROWSER_TEST_P(
// Expect a "get" event and an "observe" event respectively.
histogram_tester.ExpectTotalCount(kBrowsingTopicsApiActionTypeHistogramId,
/*expected_count=*/2);
histogram_tester.ExpectBucketCount(
kBrowsingTopicsApiActionTypeHistogramId,
GetBrowsingTopicsApiActionType(/*observe=*/false),
/*expected_count=*/1);
histogram_tester.ExpectBucketCount(
kBrowsingTopicsApiActionTypeHistogramId,
GetBrowsingTopicsApiActionType(/*observe=*/true),
/*expected_count=*/1);
histogram_tester.ExpectBucketCount(kBrowsingTopicsApiActionTypeHistogramId,
2 /*kGetViaFetchLikeApi*/,
/*expected_count=*/1);
histogram_tester.ExpectBucketCount(kBrowsingTopicsApiActionTypeHistogramId,
3 /*kObserveViaFetchLikeApi*/,
/*expected_count=*/1);
}
IN_PROC_BROWSER_TEST_P(
BrowsingTopicsSubresourceRequestTest,
SameOrigin_TopicsNotEligibleDueToUserSettings_HasObserveResponse) {
IN_PROC_BROWSER_TEST_F(
BrowsingTopicsBrowserTest,
FetchSameOrigin_TopicsNotEligibleDueToUserSettings_HasObserveResponse) {
GURL main_frame_url =
https_server_.GetURL("a.test", "/browsing_topics/empty_page.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_frame_url));
@ -1415,17 +1331,22 @@ IN_PROC_BROWSER_TEST_P(
"Observe-Browsing-Topics: ?1"));
replacement.emplace_back(std::make_pair("{{REDIRECT_HEADER}}", ""));
GURL resource_url = https_server_.GetURL(
"a.test", net::test_server::GetFilePathWithReplacements(GetRelativePath(),
replacement));
GURL fetch_url = https_server_.GetURL(
"a.test", net::test_server::GetFilePathWithReplacements(
"/browsing_topics/"
"page_with_custom_topics_header.html",
replacement));
CookieSettingsFactory::GetForProfile(browser()->profile())
->SetCookieSetting(resource_url, CONTENT_SETTING_BLOCK);
->SetCookieSetting(fetch_url, CONTENT_SETTING_BLOCK);
EXPECT_TRUE(ExecJsWithBrowsingTopicsTrue(resource_url));
EXPECT_TRUE(ExecJs(
web_contents()->GetPrimaryMainFrame(),
content::JsReplace("fetch($1, {browsingTopics: true})", fetch_url)));
std::optional<std::string> topics_header_value =
GetTopicsHeaderForRequestPath(GetRelativePath());
GetTopicsHeaderForRequestPath(
"/browsing_topics/page_with_custom_topics_header.html");
// When the request is ineligible for topics due to user settings, an empty
// list of topics will be sent in the header.
@ -1439,9 +1360,9 @@ IN_PROC_BROWSER_TEST_P(
EXPECT_EQ(api_usage_contexts.size(), 1u);
}
IN_PROC_BROWSER_TEST_P(
BrowsingTopicsSubresourceRequestTest,
CrossOrigin_TopicsEligible_SendTopics_HasObserveResponse) {
IN_PROC_BROWSER_TEST_F(
BrowsingTopicsBrowserTest,
FetchCrossOrigin_TopicsEligible_SendTopics_HasObserveResponse) {
GURL main_frame_url =
https_server_.GetURL("b.test", "/browsing_topics/empty_page.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_frame_url));
@ -1452,14 +1373,19 @@ IN_PROC_BROWSER_TEST_P(
"Observe-Browsing-Topics: ?1"));
replacement.emplace_back(std::make_pair("{{REDIRECT_HEADER}}", ""));
GURL resource_url = https_server_.GetURL(
"a.test", net::test_server::GetFilePathWithReplacements(GetRelativePath(),
replacement));
GURL fetch_url = https_server_.GetURL(
"a.test", net::test_server::GetFilePathWithReplacements(
"/browsing_topics/"
"page_with_custom_topics_header.html",
replacement));
EXPECT_TRUE(ExecJsWithBrowsingTopicsTrue(resource_url));
EXPECT_TRUE(ExecJs(
web_contents()->GetPrimaryMainFrame(),
content::JsReplace("fetch($1, {browsingTopics: true})", fetch_url)));
std::optional<std::string> topics_header_value =
GetTopicsHeaderForRequestPath(GetRelativePath());
GetTopicsHeaderForRequestPath(
"/browsing_topics/page_with_custom_topics_header.html");
EXPECT_TRUE(topics_header_value);
EXPECT_EQ(*topics_header_value, kExpectedHeaderValueForSiteB);
@ -1480,31 +1406,28 @@ IN_PROC_BROWSER_TEST_P(
EXPECT_EQ(api_usage_contexts[1].hashed_context_domain, HashedDomain(1));
}
// On an insecure site (i.e. URL with http scheme), test a fetch or image
// request with the `browsingTopics` set to true. Expect it to throw an
// exception.
IN_PROC_BROWSER_TEST_P(
BrowsingTopicsSubresourceRequestTest,
CrossOrigin_TopicsNotEligibleDueToInsecureInitiatorContext) {
// On an insecure site (i.e. URL with http scheme), test fetch request with
// the `browsingTopics` set to true. Expect it to throw an exception.
IN_PROC_BROWSER_TEST_F(
BrowsingTopicsBrowserTest,
FetchCrossOrigin_TopicsNotEligibleDueToInsecureInitiatorContext) {
GURL main_frame_url = embedded_test_server()->GetURL(
"b.test", "/browsing_topics/empty_page.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_frame_url));
GURL resource_url = https_server_.GetURL("a.test", GetRelativePath());
GURL fetch_url =
https_server_.GetURL("a.test", "/browsing_topics/empty_page.html");
if (TestFetch()) {
content::EvalJsResult result = EvalJs(
web_contents()->GetPrimaryMainFrame(),
content::JsReplace("fetch($1, {browsingTopics: true})", resource_url));
EXPECT_THAT(result.error,
testing::HasSubstr("browsingTopics: Topics operations are only "
"available in secure contexts."));
} else {
EXPECT_TRUE(ExecJsWithBrowsingTopicsTrue(resource_url));
}
content::EvalJsResult result = EvalJs(
web_contents()->GetPrimaryMainFrame(),
content::JsReplace("fetch($1, {browsingTopics: true})", fetch_url));
EXPECT_THAT(result.error,
testing::HasSubstr("browsingTopics: Topics operations are only "
"available in secure contexts."));
std::optional<std::string> topics_header_value =
GetTopicsHeaderForRequestPath(GetRelativePath());
GetTopicsHeaderForRequestPath("/browsing_topics/empty_page.html");
// Expect no topics header as the request was not eligible for topics due to
// insecure initiator context.
@ -1513,9 +1436,9 @@ IN_PROC_BROWSER_TEST_P(
// Only allow topics from origin c.test, and test fetch requests to b.test and
// c.test to verify that only c.test gets them.
IN_PROC_BROWSER_TEST_P(
BrowsingTopicsSubresourceRequestTest,
CrossOrigin_TopicsNotEligibleDueToPermissionsPolicyAgainstRequestOrigin) {
IN_PROC_BROWSER_TEST_F(
BrowsingTopicsBrowserTest,
FetchCrossOrigin_TopicsNotEligibleDueToPermissionsPolicyAgainstRequestOrigin) {
base::StringPairs allowed_origin_replacement;
allowed_origin_replacement.emplace_back(
"{{ALLOWED_ORIGIN}}", https_server_.GetOrigin("c.test").Serialize());
@ -1531,12 +1454,15 @@ IN_PROC_BROWSER_TEST_P(
{
base::HistogramTester histogram_tester;
GURL resource_url = https_server_.GetURL("a.test", GetRelativePath());
GURL fetch_url =
https_server_.GetURL("a.test", "/browsing_topics/empty_page.html");
EXPECT_TRUE(ExecJsWithBrowsingTopicsTrue(resource_url));
EXPECT_TRUE(ExecJs(
web_contents()->GetPrimaryMainFrame(),
content::JsReplace("fetch($1, {browsingTopics: true})", fetch_url)));
std::optional<std::string> topics_header_value =
GetTopicsHeaderForRequestPath(GetRelativePath());
GetTopicsHeaderForRequestPath("/browsing_topics/empty_page.html");
// No topics header was sent, as the permissions policy denied it.
EXPECT_FALSE(topics_header_value);
@ -1551,29 +1477,31 @@ IN_PROC_BROWSER_TEST_P(
{
base::HistogramTester histogram_tester;
GURL resource_url = https_server_.GetURL("c.test", GetRelativePath());
GURL fetch_url =
https_server_.GetURL("c.test", "/browsing_topics/empty_page.html");
EXPECT_TRUE(ExecJsWithBrowsingTopicsTrue(resource_url));
EXPECT_TRUE(ExecJs(
web_contents()->GetPrimaryMainFrame(),
content::JsReplace("fetch($1, {browsingTopics: true})", fetch_url)));
std::optional<std::string> topics_header_value =
GetTopicsHeaderForRequestPath(GetRelativePath());
GetTopicsHeaderForRequestPath("/browsing_topics/empty_page.html");
EXPECT_TRUE(topics_header_value);
histogram_tester.ExpectUniqueSample(
kBrowsingTopicsApiActionTypeHistogramId,
GetBrowsingTopicsApiActionType(/*observe=*/false),
/*expected_bucket_count=*/1);
histogram_tester.ExpectUniqueSample(kBrowsingTopicsApiActionTypeHistogramId,
2 /*kGetViaFetchLikeApi*/,
/*expected_bucket_count=*/1);
}
}
// On site b.test, test a fetch or image request to a.test that gets redirected
// to c.test. The topics header should be calculated for them individually (i.e.
// given that only a.test has observed the candidate topics for site b.test, the
// request to a.test should have a non-empty topics header, while the redirected
// request to c.test should have an empty topics header.)
IN_PROC_BROWSER_TEST_P(BrowsingTopicsSubresourceRequestTest,
CrossOriginWithRedirect) {
// On site b.test, test fetch request to a.test that gets redirected to c.test.
// The topics header should be calculated for them individually (i.e. given that
// only a.test has observed the candidate topics for site b.test, the request to
// a.test should have a non-empty topics header, while the redirected request to
// c.test should have an empty topics header.)
IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest,
FetchCrossOriginWithRedirect) {
base::HistogramTester histogram_tester;
GURL main_frame_url =
@ -1589,7 +1517,9 @@ IN_PROC_BROWSER_TEST_P(BrowsingTopicsSubresourceRequestTest,
GURL redirect_url = https_server_.GetURL(
"c.test", net::test_server::GetFilePathWithReplacements(
GetRedirectRelativePath(), redirect_replacement));
"/browsing_topics/"
"page_with_custom_topics_header2.html",
redirect_replacement));
base::StringPairs replacement;
replacement.emplace_back(
@ -1599,21 +1529,27 @@ IN_PROC_BROWSER_TEST_P(BrowsingTopicsSubresourceRequestTest,
replacement.emplace_back(std::make_pair("{{REDIRECT_HEADER}}",
"Location: " + redirect_url.spec()));
GURL resource_url = https_server_.GetURL(
"a.test", net::test_server::GetFilePathWithReplacements(GetRelativePath(),
replacement));
GURL fetch_url = https_server_.GetURL(
"a.test", net::test_server::GetFilePathWithReplacements(
"/browsing_topics/"
"page_with_custom_topics_header.html",
replacement));
EXPECT_TRUE(ExecJsWithBrowsingTopicsTrue(resource_url));
EXPECT_TRUE(ExecJs(
web_contents()->GetPrimaryMainFrame(),
content::JsReplace("fetch($1, {browsingTopics: true})", fetch_url)));
{
std::optional<std::string> topics_header_value =
GetTopicsHeaderForRequestPath(GetRelativePath());
GetTopicsHeaderForRequestPath(
"/browsing_topics/page_with_custom_topics_header.html");
EXPECT_TRUE(topics_header_value);
EXPECT_EQ(*topics_header_value, kExpectedHeaderValueForSiteB);
}
{
std::optional<std::string> topics_header_value =
GetTopicsHeaderForRequestPath(GetRedirectRelativePath());
GetTopicsHeaderForRequestPath(
"/browsing_topics/page_with_custom_topics_header2.html");
EXPECT_TRUE(topics_header_value);
// An empty topics header value was sent, because "c.test" did not observe
@ -1644,24 +1580,22 @@ IN_PROC_BROWSER_TEST_P(BrowsingTopicsSubresourceRequestTest,
// and the redirect respectively.
histogram_tester.ExpectTotalCount(kBrowsingTopicsApiActionTypeHistogramId,
/*expected_count=*/4);
histogram_tester.ExpectBucketCount(
kBrowsingTopicsApiActionTypeHistogramId,
GetBrowsingTopicsApiActionType(/*observe=*/false),
/*expected_count=*/2);
histogram_tester.ExpectBucketCount(
kBrowsingTopicsApiActionTypeHistogramId,
GetBrowsingTopicsApiActionType(/*observe=*/true),
/*expected_count=*/2);
histogram_tester.ExpectBucketCount(kBrowsingTopicsApiActionTypeHistogramId,
2 /*kGetViaFetchLikeApi*/,
/*expected_count=*/2);
histogram_tester.ExpectBucketCount(kBrowsingTopicsApiActionTypeHistogramId,
3 /*kObserveViaFetchLikeApi*/,
/*expected_count=*/2);
}
// On site b.test, test a fetch or image request to a.test that gets redirected
// to c.test. The topics header eligibility should be checked for them
// individually (i.e. given that the declared policy on the page only allows
// origin c.test, the request to a.test should not have the topics header, while
// the redirected request to c.test should have the topics header.)
IN_PROC_BROWSER_TEST_P(
BrowsingTopicsSubresourceRequestTest,
CrossOriginWithRedirect_InitialRequestTopicsNotEligibleDueToPermissionsPolicy) {
// On site b.test, test fetch request to a.test that gets redirected to c.test.
// The topics header eligibility should be checked for them individually (i.e.
// given that the declared policy on the page only allows origin c.test, the
// request to a.test should not have the topics header, while the redirected
// request to c.test should have the topics header.)
IN_PROC_BROWSER_TEST_F(
BrowsingTopicsBrowserTest,
FetchCrossOriginWithRedirect_InitialRequestTopicsNotEligibleDueToPermissionsPolicy) {
base::StringPairs allowed_origin_replacement;
allowed_origin_replacement.emplace_back(
"{{ALLOWED_ORIGIN}}", https_server_.GetOrigin("c.test").Serialize());
@ -1682,7 +1616,9 @@ IN_PROC_BROWSER_TEST_P(
GURL redirect_url = https_server_.GetURL(
"c.test", net::test_server::GetFilePathWithReplacements(
GetRedirectRelativePath(), redirect_replacement));
"/browsing_topics/"
"page_with_custom_topics_header2.html",
redirect_replacement));
base::StringPairs replacement;
replacement.emplace_back(
@ -1692,22 +1628,28 @@ IN_PROC_BROWSER_TEST_P(
replacement.emplace_back(std::make_pair("{{REDIRECT_HEADER}}",
"Location: " + redirect_url.spec()));
GURL resource_url = https_server_.GetURL(
"a.test", net::test_server::GetFilePathWithReplacements(GetRelativePath(),
replacement));
GURL fetch_url = https_server_.GetURL(
"a.test", net::test_server::GetFilePathWithReplacements(
"/browsing_topics/"
"page_with_custom_topics_header.html",
replacement));
EXPECT_TRUE(ExecJsWithBrowsingTopicsTrue(resource_url));
EXPECT_TRUE(ExecJs(
web_contents()->GetPrimaryMainFrame(),
content::JsReplace("fetch($1, {browsingTopics: true})", fetch_url)));
{
std::optional<std::string> topics_header_value =
GetTopicsHeaderForRequestPath(GetRelativePath());
GetTopicsHeaderForRequestPath(
"/browsing_topics/page_with_custom_topics_header.html");
// No topics header was sent, as the permissions policy denied it.
EXPECT_FALSE(topics_header_value);
}
{
std::optional<std::string> topics_header_value =
GetTopicsHeaderForRequestPath(GetRedirectRelativePath());
GetTopicsHeaderForRequestPath(
"/browsing_topics/page_with_custom_topics_header2.html");
EXPECT_TRUE(topics_header_value);
// An empty topics header value was sent, as "c.test" did not observe the
@ -1797,71 +1739,6 @@ IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest, UseCounter_Fetch) {
}
}
IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest, UseCounter_Img) {
base::HistogramTester histogram_tester;
GURL main_frame_url =
https_server_.GetURL("a.test", "/browsing_topics/empty_page.html");
GURL img_url = https_server_.GetURL(
"a.test", "/browsing_topics/topics-writable-pixel.png");
{
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_frame_url));
// Request an image with `browsingTopics` set to false. Expect no
// `kTopicsAPIImg` use counter.
EXPECT_TRUE(ExecJs(web_contents()->GetPrimaryMainFrame(),
content::JsReplace(R"(
let img = document.createElement('img');
img.src = $1;
img.browsingTopics = false;
img.decode()
.then(() => {
document.body.appendChild(img);
})
)",
img_url.spec())));
// Navigate away to flush use counters.
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)));
histogram_tester.ExpectBucketCount("Blink.UseCounter.Features",
blink::mojom::WebFeature::kTopicsAPIImg,
0);
}
{
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_frame_url));
// Request an img with `browsingTopics` set to true. Expect one
// `kTopicsAPIImg` use counter.
EXPECT_TRUE(ExecJs(web_contents()->GetPrimaryMainFrame(),
content::JsReplace(R"(
let img = document.createElement('img');
img.src = $1;
img.browsingTopics = true;
img.decode()
.then(() => {
document.body.appendChild(img);
})
)",
img_url.spec())));
// Navigate away to flush use counters.
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)));
histogram_tester.ExpectBucketCount("Blink.UseCounter.Features",
blink::mojom::WebFeature::kTopicsAPIImg,
1);
histogram_tester.ExpectBucketCount("Blink.UseCounter.Features",
blink::mojom::WebFeature::kTopicsAPIAll,
1);
}
}
// For a page that contains a static <iframe> with a "browsingtopics"
// attribute, the iframe navigation request should be eligible for topics.
IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest,
@ -2586,24 +2463,6 @@ class AttestationBrowsingTopicsBrowserTest : public BrowsingTopicsBrowserTest {
~AttestationBrowsingTopicsBrowserTest() override = default;
};
class AttestationSubresourceRequestTest
: public BrowsingTopicsSubresourceRequestTest {
public:
void SetUpOnMainThread() override {
// This test suite tests Privacy Sandbox Attestations related behaviors,
// turn off the setting that makes all APIs considered attested.
BrowsingTopicsBrowserTest::SetUpOnMainThread();
privacy_sandbox::PrivacySandboxAttestations::GetInstance()
->SetAllPrivacySandboxAttestedForTesting(false);
}
~AttestationSubresourceRequestTest() override = default;
};
INSTANTIATE_TEST_SUITE_P(All,
AttestationSubresourceRequestTest,
::testing::Bool());
// Site a.test is attested for Topics, so it should receive a valid response.
IN_PROC_BROWSER_TEST_F(AttestationBrowsingTopicsBrowserTest,
AttestedSiteCanGetBrowsingTopicsViaDocumentAPI) {
@ -2683,8 +2542,8 @@ IN_PROC_BROWSER_TEST_F(
EXPECT_FALSE(console_observer.messages().empty());
}
IN_PROC_BROWSER_TEST_P(AttestationSubresourceRequestTest,
SameOrigin_TopicsEligible_SendTopics_SiteAttested) {
IN_PROC_BROWSER_TEST_F(AttestationBrowsingTopicsBrowserTest,
FetchSameOrigin_TopicsEligible_SendTopics_SiteAttested) {
privacy_sandbox::PrivacySandboxAttestationsMap map;
map.insert_or_assign(
net::SchemefulSite(GURL("https://a.test")),
@ -2700,12 +2559,16 @@ IN_PROC_BROWSER_TEST_P(AttestationSubresourceRequestTest,
https_server_.GetURL("a.test", "/browsing_topics/empty_page.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_frame_url));
GURL resource_url = https_server_.GetURL("a.test", GetRelativePath());
GURL fetch_url = https_server_.GetURL(
"a.test", "/browsing_topics/page_with_custom_topics_header.html");
EXPECT_TRUE(ExecJsWithBrowsingTopicsTrue(resource_url));
EXPECT_TRUE(ExecJs(
web_contents()->GetPrimaryMainFrame(),
content::JsReplace("fetch($1, {browsingTopics: true})", fetch_url)));
std::optional<std::string> topics_header_value =
GetTopicsHeaderForRequestPath(GetRelativePath());
GetTopicsHeaderForRequestPath(
"/browsing_topics/page_with_custom_topics_header.html");
EXPECT_TRUE(topics_header_value);
EXPECT_EQ(*topics_header_value, kExpectedHeaderValueForSiteA);
@ -2713,8 +2576,8 @@ IN_PROC_BROWSER_TEST_P(AttestationSubresourceRequestTest,
EXPECT_TRUE(console_observer.messages().empty());
}
IN_PROC_BROWSER_TEST_P(AttestationSubresourceRequestTest,
SameOrigin_TopicsEligible_SiteNotAttested) {
IN_PROC_BROWSER_TEST_F(AttestationBrowsingTopicsBrowserTest,
FetchSameOrigin_TopicsEligible_SiteNotAttested) {
privacy_sandbox::PrivacySandboxAttestationsMap map;
map.insert_or_assign(
net::SchemefulSite(GURL("https://b.test")),
@ -2730,12 +2593,16 @@ IN_PROC_BROWSER_TEST_P(AttestationSubresourceRequestTest,
https_server_.GetURL("a.test", "/browsing_topics/empty_page.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_frame_url));
GURL resource_url = https_server_.GetURL("a.test", GetRelativePath());
GURL fetch_url = https_server_.GetURL(
"a.test", "/browsing_topics/page_with_custom_topics_header.html");
EXPECT_TRUE(ExecJsWithBrowsingTopicsTrue(resource_url));
EXPECT_TRUE(ExecJs(
web_contents()->GetPrimaryMainFrame(),
content::JsReplace("fetch($1, {browsingTopics: true})", fetch_url)));
std::optional<std::string> topics_header_value =
GetTopicsHeaderForRequestPath(GetRelativePath());
GetTopicsHeaderForRequestPath(
"/browsing_topics/page_with_custom_topics_header.html");
EXPECT_EQ(topics_header_value, kExpectedHeaderValueForEmptyTopics);
@ -2743,8 +2610,9 @@ IN_PROC_BROWSER_TEST_P(AttestationSubresourceRequestTest,
EXPECT_FALSE(console_observer.messages().empty());
}
IN_PROC_BROWSER_TEST_P(AttestationSubresourceRequestTest,
SameOrigin_TopicsEligible_SiteAttested_MismatchedMap) {
IN_PROC_BROWSER_TEST_F(
AttestationBrowsingTopicsBrowserTest,
FetchSameOrigin_TopicsEligible_SiteAttested_MismatchedMap) {
privacy_sandbox::PrivacySandboxAttestationsMap map;
map.insert_or_assign(net::SchemefulSite(GURL("https://a.test")),
privacy_sandbox::PrivacySandboxAttestationsGatedAPISet{
@ -2760,12 +2628,16 @@ IN_PROC_BROWSER_TEST_P(AttestationSubresourceRequestTest,
https_server_.GetURL("a.test", "/browsing_topics/empty_page.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_frame_url));
GURL resource_url = https_server_.GetURL("a.test", GetRelativePath());
GURL fetch_url = https_server_.GetURL(
"a.test", "/browsing_topics/page_with_custom_topics_header.html");
EXPECT_TRUE(ExecJsWithBrowsingTopicsTrue(resource_url));
EXPECT_TRUE(ExecJs(
web_contents()->GetPrimaryMainFrame(),
content::JsReplace("fetch($1, {browsingTopics: true})", fetch_url)));
std::optional<std::string> topics_header_value =
GetTopicsHeaderForRequestPath(GetRelativePath());
GetTopicsHeaderForRequestPath(
"/browsing_topics/page_with_custom_topics_header.html");
EXPECT_EQ(topics_header_value, kExpectedHeaderValueForEmptyTopics);
@ -2775,9 +2647,9 @@ IN_PROC_BROWSER_TEST_P(AttestationSubresourceRequestTest,
// Site a.test is attested, so when an x-origin request is made to it from
// site b.test, a.test should still include a topics header.
IN_PROC_BROWSER_TEST_P(
AttestationSubresourceRequestTest,
CrossOrigin_TopicsEligible_SendTopics_HasObserveResponse_SiteAttested) {
IN_PROC_BROWSER_TEST_F(
AttestationBrowsingTopicsBrowserTest,
FetchCrossOrigin_TopicsEligible_SendTopics_HasObserveResponse_SiteAttested) {
privacy_sandbox::PrivacySandboxAttestationsMap map;
map.insert_or_assign(
net::SchemefulSite(GURL("https://a.test")),
@ -2799,14 +2671,19 @@ IN_PROC_BROWSER_TEST_P(
"Observe-Browsing-Topics: ?1"));
replacement.emplace_back(std::make_pair("{{REDIRECT_HEADER}}", ""));
GURL resource_url = https_server_.GetURL(
"a.test", net::test_server::GetFilePathWithReplacements(GetRelativePath(),
replacement));
GURL fetch_url = https_server_.GetURL(
"a.test", net::test_server::GetFilePathWithReplacements(
"/browsing_topics/"
"page_with_custom_topics_header.html",
replacement));
EXPECT_TRUE(ExecJsWithBrowsingTopicsTrue(resource_url));
EXPECT_TRUE(ExecJs(
web_contents()->GetPrimaryMainFrame(),
content::JsReplace("fetch($1, {browsingTopics: true})", fetch_url)));
std::optional<std::string> topics_header_value =
GetTopicsHeaderForRequestPath(GetRelativePath());
GetTopicsHeaderForRequestPath(
"/browsing_topics/page_with_custom_topics_header.html");
EXPECT_TRUE(topics_header_value);
EXPECT_EQ(*topics_header_value, kExpectedHeaderValueForSiteB);
@ -2831,8 +2708,8 @@ IN_PROC_BROWSER_TEST_P(
// Site a.test is not attested, so this should not generate a Topics header in a
// x-origin fetch to site a.test.
IN_PROC_BROWSER_TEST_P(AttestationSubresourceRequestTest,
CrossOrigin_TopicsEligible_SiteNotAttested) {
IN_PROC_BROWSER_TEST_F(AttestationBrowsingTopicsBrowserTest,
FetchCrossOrigin_TopicsEligible_SiteNotAttested) {
privacy_sandbox::PrivacySandboxAttestationsMap map;
map.insert_or_assign(
net::SchemefulSite(GURL("https://b.test")),
@ -2854,14 +2731,19 @@ IN_PROC_BROWSER_TEST_P(AttestationSubresourceRequestTest,
"Observe-Browsing-Topics: ?1"));
replacement.emplace_back(std::make_pair("{{REDIRECT_HEADER}}", ""));
GURL resource_url = https_server_.GetURL(
"a.test", net::test_server::GetFilePathWithReplacements(GetRelativePath(),
replacement));
GURL fetch_url = https_server_.GetURL(
"a.test", net::test_server::GetFilePathWithReplacements(
"/browsing_topics/"
"page_with_custom_topics_header.html",
replacement));
EXPECT_TRUE(ExecJsWithBrowsingTopicsTrue(resource_url));
EXPECT_TRUE(ExecJs(
web_contents()->GetPrimaryMainFrame(),
content::JsReplace("fetch($1, {browsingTopics: true})", fetch_url)));
std::optional<std::string> topics_header_value =
GetTopicsHeaderForRequestPath(GetRelativePath());
GetTopicsHeaderForRequestPath(
"/browsing_topics/page_with_custom_topics_header.html");
EXPECT_EQ(topics_header_value, kExpectedHeaderValueForEmptyTopics);
@ -2875,11 +2757,11 @@ IN_PROC_BROWSER_TEST_P(AttestationSubresourceRequestTest,
EXPECT_FALSE(console_observer.messages().empty());
}
// Site a.test is attested, but not for Topics, so the fetch/img request to
// a.test should not get a header.
IN_PROC_BROWSER_TEST_P(
AttestationSubresourceRequestTest,
CrossOrigin_TopicsEligible_SiteNotAttested_MismatchedMap) {
// Site a.test is attested, but not for Topics, so the fetch request to a.test
// should not get a header.
IN_PROC_BROWSER_TEST_F(
AttestationBrowsingTopicsBrowserTest,
FetchCrossOrigin_TopicsEligible_SiteNotAttested_MismatchedMap) {
privacy_sandbox::PrivacySandboxAttestationsMap map;
map.insert_or_assign(net::SchemefulSite(GURL("https://a.test")),
privacy_sandbox::PrivacySandboxAttestationsGatedAPISet{
@ -2901,14 +2783,19 @@ IN_PROC_BROWSER_TEST_P(
"Observe-Browsing-Topics: ?1"));
replacement.emplace_back(std::make_pair("{{REDIRECT_HEADER}}", ""));
GURL resource_url = https_server_.GetURL(
"a.test", net::test_server::GetFilePathWithReplacements(GetRelativePath(),
replacement));
GURL fetch_url = https_server_.GetURL(
"a.test", net::test_server::GetFilePathWithReplacements(
"/browsing_topics/"
"page_with_custom_topics_header.html",
replacement));
EXPECT_TRUE(ExecJsWithBrowsingTopicsTrue(resource_url));
EXPECT_TRUE(ExecJs(
web_contents()->GetPrimaryMainFrame(),
content::JsReplace("fetch($1, {browsingTopics: true})", fetch_url)));
std::optional<std::string> topics_header_value =
GetTopicsHeaderForRequestPath(GetRelativePath());
GetTopicsHeaderForRequestPath(
"/browsing_topics/page_with_custom_topics_header.html");
EXPECT_EQ(topics_header_value, kExpectedHeaderValueForEmptyTopics);

Binary file not shown.

Before

(image error) Size: 119 B

@ -1,4 +0,0 @@
HTTP/1.1 {{STATUS}}
Access-Control-Allow-Origin: *
{{OBSERVE_BROWSING_TOPICS_HEADER}}
{{REDIRECT_HEADER}}

Binary file not shown.

Before

(image error) Size: 119 B

@ -1,4 +0,0 @@
HTTP/1.1 {{STATUS}}
Access-Control-Allow-Origin: *
{{OBSERVE_BROWSING_TOPICS_HEADER}}
{{REDIRECT_HEADER}}

@ -270,14 +270,7 @@ enum class BrowsingTopicsApiActionType {
// <iframe src=[url] browsingtopics> request.
kObserveViaIframeAttributeApi = 5,
// Get topics via <img src=[url] browsingtopics>.
kGetViaImgAttributeApi = 6,
// Observe topics via the "Sec-Browsing-Topics: ?1" response header for the
// <img src=[url] browsingtopics> request.
kObserveViaImgAttributeApi = 7,
kMaxValue = kObserveViaImgAttributeApi,
kMaxValue = kObserveViaIframeAttributeApi,
};
void RecordBrowsingTopicsApiActionTypeMetrics(ApiCallerSource caller_source,
@ -321,24 +314,6 @@ void RecordBrowsingTopicsApiActionTypeMetrics(ApiCallerSource caller_source,
return;
}
if (caller_source == ApiCallerSource::kImgAttribute) {
if (get_topics) {
DCHECK(!observe);
base::UmaHistogramEnumeration(
kBrowsingTopicsApiActionTypeHistogramId,
BrowsingTopicsApiActionType::kGetViaImgAttributeApi);
return;
}
DCHECK(observe);
base::UmaHistogramEnumeration(
kBrowsingTopicsApiActionTypeHistogramId,
BrowsingTopicsApiActionType::kObserveViaImgAttributeApi);
return;
}
DCHECK_EQ(caller_source, ApiCallerSource::kFetch);
if (get_topics) {

@ -40,9 +40,6 @@ enum class ApiCallerSource {
// The API usage is from <iframe src=[url] browsingtopics>.
kIframeAttribute,
// The API usage is from <img src=[url] browsingtopics>.
kImgAttribute,
};
// Represents the different reasons why the topics API access is denied. These

@ -4,7 +4,6 @@
#include "content/browser/browsing_topics/browsing_topics_url_loader_interceptor.h"
#include "components/browsing_topics/common/common_types.h"
#include "content/browser/browsing_topics/header_util.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/public/browser/content_browser_client.h"
@ -171,9 +170,7 @@ void BrowsingTopicsURLLoaderInterceptor::PopulateRequestOrRedirectHeaders(
std::vector<blink::mojom::EpochTopicPtr> topics;
topics_eligible_ = GetContentClient()->browser()->HandleTopicsWebApi(
origin, request_initiator_frame->GetMainFrame(),
resource_request_->is_fetch_like_api
? browsing_topics::ApiCallerSource::kFetch
: browsing_topics::ApiCallerSource::kImgAttribute,
browsing_topics::ApiCallerSource::kFetch,
/*get_topics=*/true,
/*observe=*/false, topics);
@ -203,11 +200,9 @@ void BrowsingTopicsURLLoaderInterceptor::ProcessRedirectOrResponseHeaders(
return;
}
HandleTopicsEligibleResponse(
head->parsed_headers, url::Origin::Create(url_), *rfh,
resource_request_->is_fetch_like_api
? browsing_topics::ApiCallerSource::kFetch
: browsing_topics::ApiCallerSource::kImgAttribute);
HandleTopicsEligibleResponse(head->parsed_headers,
url::Origin::Create(url_), *rfh,
browsing_topics::ApiCallerSource::kFetch);
topics_eligible_ = false;
}

@ -5,7 +5,6 @@
#include "content/browser/browsing_topics/header_util.h"
#include "base/strings/strcat.h"
#include "components/browsing_topics/common/common_types.h"
#include "components/browsing_topics/common/semantic_tree.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/public/browser/content_browser_client.h"
@ -149,8 +148,7 @@ void HandleTopicsEligibleResponse(
RenderFrameHost& request_initiator_frame,
browsing_topics::ApiCallerSource caller_source) {
DCHECK(caller_source == browsing_topics::ApiCallerSource::kFetch ||
caller_source == browsing_topics::ApiCallerSource::kIframeAttribute ||
caller_source == browsing_topics::ApiCallerSource::kImgAttribute);
caller_source == browsing_topics::ApiCallerSource::kIframeAttribute);
if (!parsed_headers || !parsed_headers->observe_browsing_topics) {
return;

@ -395,9 +395,6 @@ void SetRuntimeFeaturesFromChromiumFeatures() {
kSetOnlyIfOverridden},
{"TopicsDocumentAPI",
raw_ref(features::kPrivacySandboxAdsAPIsM1Override)},
{"TopicsImgAPI", raw_ref(features::kPrivacySandboxAdsAPIsOverride),
kSetOnlyIfOverridden},
{"TopicsImgAPI", raw_ref(features::kPrivacySandboxAdsAPIsM1Override)},
{"TouchTextEditingRedesign",
raw_ref(features::kTouchTextEditingRedesign)},
{"TrustedTypesFromLiteral",
@ -586,7 +583,6 @@ void ResolveInvalidConfigurations() {
<< blink::features::kBrowsingTopics.name << " in addition.";
WebRuntimeFeatures::EnableTopicsAPI(false);
WebRuntimeFeatures::EnableTopicsDocumentAPI(false);
WebRuntimeFeatures::EnableTopicsImgAPI(false);
} else {
if (!base::FeatureList::IsEnabled(
blink::features::kBrowsingTopicsDocumentAPI)) {

@ -4619,7 +4619,6 @@ enum WebFeature {
kInjectionMitigatedContextSubFrame = 5233,
kInjectionMitigatedContextMainFrame = 5234,
kCanvasTextDirectionConflict = 5235,
kTopicsAPIImg = 5236,
// Add new features immediately above this line. Don't change assigned
// numbers of any item, and don't reuse removed slots. Also don't add extra

@ -63,9 +63,6 @@
[MeasureAs=HTMLImageElementY] readonly attribute long y;
[CallWith=ScriptState, RaisesException] Promise<undefined> decode();
// Topics API (https://github.com/patcg-individual-drafts/topics)
[RuntimeEnabled=TopicsImgAPI, SecureContext, CEReactions, Reflect] attribute boolean browsingTopics;
};
HTMLImageElement includes HTMLAttributionSrcElementUtils;

@ -369,11 +369,6 @@ class TokenPreloadScanner::StartTagScanner {
request->SetSharedStorageWritableOptedIn(true);
}
if (browsing_topics_attr_set_) {
DCHECK(is_img);
request->SetBrowsingTopicsEligible(true);
}
return request;
}
@ -456,8 +451,6 @@ class TokenPreloadScanner::StartTagScanner {
attributionsrc_attr_set_ = true;
} else if (Match(attribute_name, html_names::kSharedstoragewritableAttr)) {
shared_storage_writable_opted_in_ = true;
} else if (Match(attribute_name, html_names::kBrowsingtopicsAttr)) {
browsing_topics_attr_set_ = true;
} else if (use_data_src_attr_match_for_image_ &&
Match(attribute_name, html_names::kDataSrcAttr) &&
img_src_url_.IsNull()) {
@ -825,7 +818,6 @@ class TokenPreloadScanner::StartTagScanner {
const HashSet<String>* disabled_image_types_;
bool attributionsrc_attr_set_ = false;
bool shared_storage_writable_opted_in_ = false;
bool browsing_topics_attr_set_ = false;
std::optional<float> resource_width_;
std::optional<float> resource_height_;
features::LcppPreloadLazyLoadImageType preload_lazy_load_image_type_;

@ -130,13 +130,6 @@ struct SharedStorageWritableTestCase {
bool expected_shared_storage_writable_opted_in;
};
struct BrowsingTopicsWritableTestCase {
bool use_secure_document_url;
const char* base_url;
const char* input_html;
bool expected_browsing_topics;
};
class HTMLMockHTMLResourcePreloader : public ResourcePreloader {
public:
explicit HTMLMockHTMLResourcePreloader(const KURL& document_url)
@ -316,16 +309,6 @@ class HTMLMockHTMLResourcePreloader : public ResourcePreloader {
resource->GetResourceRequest().GetSharedStorageWritableOptedIn());
}
void BrowsingTopicsRequestVerification(Document* document,
bool expected_browsing_topics) {
ASSERT_TRUE(preload_request_.get());
Resource* resource = preload_request_->Start(document);
ASSERT_TRUE(resource);
EXPECT_EQ(expected_browsing_topics,
resource->GetResourceRequest().GetBrowsingTopics());
}
protected:
void Preload(std::unique_ptr<PreloadRequest> preload_request) override {
preload_request_ = std::move(preload_request);
@ -563,20 +546,6 @@ class HTMLPreloadScannerTest : public PageTestBase {
&GetDocument(), test_case.expected_shared_storage_writable_opted_in);
}
void Test(BrowsingTopicsWritableTestCase test_case) {
SCOPED_TRACE(base::StringPrintf("Use secure doc URL: %d; HTML: '%s'",
test_case.use_secure_document_url,
test_case.input_html));
HTMLMockHTMLResourcePreloader preloader(GetDocument().Url());
KURL base_url(test_case.base_url);
scanner_->AppendToEnd(String(test_case.input_html));
std::unique_ptr<PendingPreloadData> preload_data = scanner_->Scan(base_url);
preloader.TakePreloadData(std::move(preload_data));
preloader.BrowsingTopicsRequestVerification(
&GetDocument(), test_case.expected_browsing_topics);
}
private:
std::unique_ptr<HTMLPreloadScanner> scanner_;
};
@ -2021,42 +1990,4 @@ TEST_F(HTMLPreloadScannerTest, PreloadScanDisabled_NoPreloads) {
}
}
TEST_F(HTMLPreloadScannerTest, testBrowsingTopics) {
WebRuntimeFeaturesBase::EnableTopicsAPI(true);
static constexpr bool kSecureDocumentUrl = true;
static constexpr bool kInsecureDocumentUrl = false;
static constexpr char kSecureBaseURL[] = "https://example.test";
static constexpr char kInsecureBaseURL[] = "http://example.test";
BrowsingTopicsWritableTestCase test_cases[] = {
// Insecure context
{kInsecureDocumentUrl, kSecureBaseURL,
"<img src='/image' browsingtopics>",
/*expected_browsing_topics=*/false},
// No browsingtopics attribute
{kSecureDocumentUrl, kSecureBaseURL, "<img src='/image'>",
/*expected_browsing_topics=*/false},
// Irrelevant element type
{kSecureDocumentUrl, kSecureBaseURL,
"<video poster='/image' browsingtopics>",
/*expected_browsing_topics=*/false},
// Secure context, browsingtopics attribute
// Base (initial) URL does not affect SharedStorageWritable eligibility
{kSecureDocumentUrl, kInsecureBaseURL,
"<img src='/image' browsingtopics>",
/*expected_browsing_topics=*/true},
// Secure context, browsingtopics attribute
{kSecureDocumentUrl, kSecureBaseURL, "<img src='/image' browsingtopics>",
/*expected_browsing_topics=*/true},
};
for (const auto& test_case : test_cases) {
RunSetUp(kViewportDisabled, kPreloadEnabled,
network::mojom::ReferrerPolicy::kDefault,
/*use_secure_document_url=*/test_case.use_secure_document_url);
Test(test_case);
}
}
} // namespace blink

@ -140,12 +140,6 @@ Resource* PreloadRequest::Start(Document* document) {
UseCounter::Count(document, WebFeature::kSharedStorageAPI_Image_Attribute);
}
bool browsing_topics =
browsing_topics_eligible_ && RuntimeEnabledFeatures::TopicsAPIEnabled() &&
document->domWindow()->IsSecureContext() &&
!document->domWindow()->GetSecurityOrigin()->IsOpaque();
resource_request.SetBrowsingTopics(browsing_topics);
ResourceLoaderOptions options(document->domWindow()->GetCurrentWorld());
options.initiator_info = initiator_info;
FetchParameters params(std::move(resource_request), options);

@ -159,14 +159,6 @@ class CORE_EXPORT PreloadRequest {
shared_storage_writable_opted_in_ = opted_in;
}
// Set whether the preload request is eligible for the Browsing Topics API.
//
// See https://github.com/patcg-individual-drafts/topics/blob/main/README.md
// for the latest version of the Topics API explainer.
void SetBrowsingTopicsEligible(bool flag) {
browsing_topics_eligible_ = flag;
}
bool IsPotentiallyLCPElement() const { return is_potentially_lcp_element_; }
bool IsPotentiallyLCPInfluencer() const {
@ -225,7 +217,6 @@ class CORE_EXPORT PreloadRequest {
bool is_potentially_lcp_element_ = false;
bool is_potentially_lcp_influencer_ = false;
bool shared_storage_writable_opted_in_ = false;
bool browsing_topics_eligible_ = false;
};
typedef Vector<std::unique_ptr<PreloadRequest>> PreloadRequestStream;

@ -507,14 +507,6 @@ void ImageLoader::DoUpdateFromElement(const DOMWrapperWorld* world,
!SecurityOrigin::Create(url)->IsOpaque();
resource_request.SetSharedStorageWritableOptedIn(
shared_storage_writable_opted_in);
if (GetElement()->FastHasAttribute(html_names::kBrowsingtopicsAttr) &&
RuntimeEnabledFeatures::TopicsAPIEnabled(
GetElement()->GetExecutionContext()) &&
GetElement()->GetExecutionContext()->IsSecureContext()) {
resource_request.SetBrowsingTopics(true);
UseCounter::Count(document, mojom::blink::WebFeature::kTopicsAPIImg);
UseCounter::Count(document, mojom::blink::WebFeature::kTopicsAPIAll);
}
}
bool page_is_being_dismissed =

@ -4297,14 +4297,6 @@
public: true,
status: "stable",
},
{
// This feature allows calling the Topics API via an image
// attribute.
name: "TopicsImgAPI",
base_feature: "none",
public: true,
status: "experimental",
},
// This is a killswitch for the behavior where popover.showPopover() and
// dialog.showModal() throw DOM exceptions if the document isn't active.
// This landed in M132, and can be removed in M134.

@ -692,7 +692,6 @@ html element img
property alt
property attributionSrc
property border
property browsingTopics
property complete
property crossOrigin
property currentSrc

@ -3895,7 +3895,6 @@ interface HTMLImageElement : HTMLElement
getter alt
getter attributionSrc
getter border
getter browsingTopics
getter complete
getter crossOrigin
getter currentSrc
@ -3926,7 +3925,6 @@ interface HTMLImageElement : HTMLElement
setter alt
setter attributionSrc
setter border
setter browsingTopics
setter crossOrigin
setter decoding
setter fetchPriority
@ -5006,7 +5004,6 @@ interface Image
getter alt
getter attributionSrc
getter border
getter browsingTopics
getter complete
getter crossOrigin
getter currentSrc
@ -5037,7 +5034,6 @@ interface Image
setter alt
setter attributionSrc
setter border
setter browsingTopics
setter crossOrigin
setter decoding
setter fetchPriority

@ -728,7 +728,6 @@ html element img
property alt
property attributionSrc
property border
property browsingTopics
property complete
property crossOrigin
property currentSrc

@ -4329,7 +4329,6 @@ interface HTMLImageElement : HTMLElement
getter alt
getter attributionSrc
getter border
getter browsingTopics
getter complete
getter crossOrigin
getter currentSrc
@ -4360,7 +4359,6 @@ interface HTMLImageElement : HTMLElement
setter alt
setter attributionSrc
setter border
setter browsingTopics
setter crossOrigin
setter decoding
setter fetchPriority
@ -5486,7 +5484,6 @@ interface Image
getter alt
getter attributionSrc
getter border
getter browsingTopics
getter complete
getter crossOrigin
getter currentSrc
@ -5517,7 +5514,6 @@ interface Image
setter alt
setter attributionSrc
setter border
setter browsingTopics
setter crossOrigin
setter decoding
setter fetchPriority

@ -10952,7 +10952,6 @@ Called by update_use_counter_feature_enum.py.-->
<int value="5233" label="InjectionMitigatedContextSubFrame"/>
<int value="5234" label="InjectionMitigatedContextMainFrame"/>
<int value="5235" label="CanvasTextDirectionConflict"/>
<int value="5236" label="TopicsAPIImg"/>
</enum>
<!-- LINT.ThenChange(//third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom:WebFeature) -->

@ -33,8 +33,6 @@ chromium-metrics-reviews@google.com.
<int value="3" label="observe via fetch-like api"/>
<int value="4" label="get via iframe attribute api"/>
<int value="5" label="observe via iframe attribute api"/>
<int value="6" label="get via img attribute api"/>
<int value="7" label="observe via img attribute api"/>
</enum>
<enum name="BrowsingTopicsCalculatorResultStatus">