Create HttpErrorTabHelper to track 3PC Breakage Indicator UKM
This CL creates a `WebContentsObserver` that is used to track cases of
Http Errors as an indicator of possible third-party cookie breakage. This metric is added as a case to the pre-existing UKM
`ThirdPartyCookies.BreakageIndicator`.
Bug: 1463370
Change-Id: Icd5dc766892ddd628e641e60b25856073001373c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4845630
Reviewed-by: Chris Fredrickson <cfredric@chromium.org>
Reviewed-by: Ben Kelly <wanderview@chromium.org>
Reviewed-by: Brendon Tiszka <tiszka@chromium.org>
Commit-Queue: Sam LeDoux <sledoux@google.com>
Cr-Commit-Position: refs/heads/main@{#1216318}
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index f45cffaa..6041acc 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1847,6 +1847,8 @@
"tpcd/heuristics/opener_heuristic_tab_helper.h",
"tpcd/heuristics/opener_heuristic_utils.cc",
"tpcd/heuristics/opener_heuristic_utils.h",
+ "tpcd/http_error_observer/http_error_tab_helper.cc",
+ "tpcd/http_error_observer/http_error_tab_helper.h",
"tpcd/metadata/devtools_observer.cc",
"tpcd/metadata/devtools_observer.h",
"tpcd/metadata/updater_service.cc",
diff --git a/chrome/browser/tpcd/http_error_observer/BUILD.gn b/chrome/browser/tpcd/http_error_observer/BUILD.gn
new file mode 100644
index 0000000..9908afe
--- /dev/null
+++ b/chrome/browser/tpcd/http_error_observer/BUILD.gn
@@ -0,0 +1,22 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("browser_tests") {
+ testonly = true
+ sources = [ "http_error_tab_helper_browsertest.cc" ]
+
+ defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+
+ deps = [
+ "//base",
+ "//chrome/browser",
+ "//chrome/test:test_support",
+ "//components/ukm:test_support",
+ "//content/test:browsertest_support",
+ "//content/test:test_support",
+ "//net:test_support",
+ "//services/metrics/public/cpp:ukm_builders",
+ "//testing/gtest",
+ ]
+}
diff --git a/chrome/browser/tpcd/http_error_observer/http_error_tab_helper.cc b/chrome/browser/tpcd/http_error_observer/http_error_tab_helper.cc
new file mode 100644
index 0000000..15f7572
--- /dev/null
+++ b/chrome/browser/tpcd/http_error_observer/http_error_tab_helper.cc
@@ -0,0 +1,68 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/tpcd/http_error_observer/http_error_tab_helper.h"
+
+#include "chrome/browser/content_settings/cookie_settings_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/content_settings/browser/page_specific_content_settings.h"
+#include "components/content_settings/core/browser/cookie_settings.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_user_data.h"
+#include "net/cookies/cookie_util.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
+#include "services/metrics/public/cpp/ukm_recorder.h"
+#include "services/metrics/public/cpp/ukm_source_id.h"
+#include "third_party/blink/public/mojom/loader/resource_load_info.mojom.h"
+
+HttpErrorTabHelper::~HttpErrorTabHelper() = default;
+
+// This override of the ResourceLoadComplete Method will be used to record
+// instances of the ThirdPartyCookiesBreakageIndicator UKM
+void HttpErrorTabHelper::ResourceLoadComplete(
+ content::RenderFrameHost* render_frame_host,
+ const content::GlobalRequestID& request_id,
+ const blink::mojom::ResourceLoadInfo& resource_load_info) {
+ // The response code will determine whether or not we record the metric. Only
+ // record for https status codes indicating a server or client error.
+ int response_code = resource_load_info.http_status_code;
+ if (response_code < 400 || response_code >= 600) {
+ return;
+ }
+ Profile* profile =
+ Profile::FromBrowserContext(render_frame_host->GetBrowserContext());
+ scoped_refptr<content_settings::CookieSettings> cs =
+ CookieSettingsFactory::GetForProfile(profile);
+
+ // For this metric, we define "cookies blocked in settings" based on the
+ // global opt-in to third-party cookie blocking as well as no overriding
+ // content setting on the top-level site.
+ bool cookies_blocked_in_settings =
+ cs && cs->ShouldBlockThirdPartyCookies() &&
+ !cs->IsThirdPartyAccessAllowed(resource_load_info.final_url, nullptr);
+
+ // Also measure if 3P cookies were actually blocked on the site.
+ content_settings::PageSpecificContentSettings* pscs =
+ content_settings::PageSpecificContentSettings::GetForFrame(
+ render_frame_host);
+ bool cookies_blocked =
+ pscs && pscs->blocked_local_shared_objects().GetObjectCount() > 0;
+
+ ukm::builders::ThirdPartyCookies_BreakageIndicator(
+ web_contents()->GetPrimaryMainFrame()->GetPageUkmSourceId())
+ .SetBreakageIndicatorType(
+ static_cast<int>(net::cookie_util::BreakageIndicatorType::HTTP_ERROR))
+ .SetTPCBlocked(cookies_blocked)
+ .SetTPCBlockedInSettings(cookies_blocked_in_settings)
+ .Record(ukm::UkmRecorder::Get());
+}
+
+HttpErrorTabHelper::HttpErrorTabHelper(content::WebContents* web_contents)
+ : content::WebContentsObserver(web_contents),
+ content::WebContentsUserData<HttpErrorTabHelper>(*web_contents) {
+ CHECK(web_contents);
+}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(HttpErrorTabHelper);
diff --git a/chrome/browser/tpcd/http_error_observer/http_error_tab_helper.h b/chrome/browser/tpcd/http_error_observer/http_error_tab_helper.h
new file mode 100644
index 0000000..775777d
--- /dev/null
+++ b/chrome/browser/tpcd/http_error_observer/http_error_tab_helper.h
@@ -0,0 +1,38 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_TPCD_HTTP_ERROR_OBSERVER_HTTP_ERROR_TAB_HELPER_H_
+#define CHROME_BROWSER_TPCD_HTTP_ERROR_OBSERVER_HTTP_ERROR_TAB_HELPER_H_
+
+#include "base/sequence_checker.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+class HttpErrorTabHelper
+ : public content::WebContentsObserver,
+ public content::WebContentsUserData<HttpErrorTabHelper> {
+ public:
+ HttpErrorTabHelper(const HttpErrorTabHelper&) = delete;
+ HttpErrorTabHelper& operator=(const HttpErrorTabHelper&) = delete;
+ ~HttpErrorTabHelper() override;
+
+ // WebContentsObserver:
+ void ResourceLoadComplete(
+ content::RenderFrameHost* render_frame_host,
+ const content::GlobalRequestID& request_id,
+ const blink::mojom::ResourceLoadInfo& resource_load_info) override;
+
+ protected:
+ explicit HttpErrorTabHelper(content::WebContents* web_contents);
+
+ private:
+ friend class content::WebContentsUserData<HttpErrorTabHelper>;
+
+ WEB_CONTENTS_USER_DATA_KEY_DECL();
+
+ SEQUENCE_CHECKER(sequence_checker_);
+};
+
+#endif // CHROME_BROWSER_TPCD_HTTP_ERROR_OBSERVER_HTTP_ERROR_TAB_HELPER_H_
diff --git a/chrome/browser/tpcd/http_error_observer/http_error_tab_helper_browsertest.cc b/chrome/browser/tpcd/http_error_observer/http_error_tab_helper_browsertest.cc
new file mode 100644
index 0000000..273d812a
--- /dev/null
+++ b/chrome/browser/tpcd/http_error_observer/http_error_tab_helper_browsertest.cc
@@ -0,0 +1,283 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/tpcd/http_error_observer/http_error_tab_helper.h"
+
+#include "base/test/metrics/histogram_tester.h"
+#include "chrome/browser/content_settings/cookie_settings_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/ui_features.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/content_settings/core/browser/cookie_settings.h"
+#include "components/content_settings/core/common/pref_names.h"
+#include "components/prefs/pref_service.h"
+#include "components/ukm/test_ukm_recorder.h"
+#include "content/public/common/content_paths.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/content_browser_test_utils.h"
+#include "content/public/test/test_navigation_observer.h"
+#include "net/cookies/cookie_util.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/test/embedded_test_server/controllable_http_response.h"
+#include "net/test/embedded_test_server/default_handlers.h"
+#include "third_party/blink/public/mojom/loader/resource_load_info.mojom.h"
+
+constexpr char kHostA[] = "a.test";
+constexpr char kHostB[] = "b.test";
+
+// Handles Favicon requests so they don't produce a 404 and augment http error
+// metrics during a test
+std::unique_ptr<net::test_server::HttpResponse> HandleFaviconRequest(
+ const net::test_server::HttpRequest& request) {
+ if (request.relative_url != "/favicon.ico") {
+ return nullptr;
+ }
+ // The response doesn't have to be a valid favicon to avoid logging a
+ // console error. Any 200 response will do.
+ return std::make_unique<net::test_server::BasicHttpResponse>();
+}
+
+class HTTPErrProcBrowserTest : public InProcessBrowserTest {
+ public:
+ HTTPErrProcBrowserTest()
+ : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
+
+ net::EmbeddedTestServer* https_server() { return &https_server_; }
+
+ void SetUpOnMainThread() override {
+ host_resolver()->AddRule("*", "127.0.0.1");
+ https_server_.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
+ https_server_.AddDefaultHandlers(GetChromeTestDataDir());
+ https_server_.RegisterRequestHandler(
+ base::BindRepeating(&HandleFaviconRequest));
+ ASSERT_TRUE(https_server_.Start());
+ }
+
+ // Sets third party cookie blocking in settings
+ void SetThirdPartyCookieBlocking(bool enabled) {
+ browser()->profile()->GetPrefs()->SetInteger(
+ prefs::kCookieControlsMode,
+ static_cast<int>(
+ enabled ? content_settings::CookieControlsMode::kBlockThirdParty
+ : content_settings::CookieControlsMode::kOff));
+ scoped_refptr<content_settings::CookieSettings> settings =
+ CookieSettingsFactory::GetForProfile(browser()->profile());
+ ASSERT_EQ(settings->ShouldBlockThirdPartyCookies(), enabled);
+ }
+
+ // Checks that the HTTP Error has been properly recorded in the metrics
+ void CheckServerErrBreakageMetrics(
+ ukm::TestAutoSetUkmRecorder& ukm_recorder,
+ size_t size,
+ size_t index,
+ bool blocked,
+ bool settings_blocked,
+ const base::Location& location = FROM_HERE) {
+ auto entries = ukm_recorder.GetEntries(
+ "ThirdPartyCookies.BreakageIndicator",
+ {"BreakageIndicatorType", "TPCBlocked", "TPCBlockedInSettings"});
+ EXPECT_EQ(entries.size(), size)
+ << "(expected at " << location.ToString() << ")";
+ EXPECT_EQ(
+ entries.at(index).metrics.at("BreakageIndicatorType"),
+ static_cast<int>(net::cookie_util::BreakageIndicatorType::HTTP_ERROR))
+ << "(expected at " << location.ToString() << ")";
+ EXPECT_EQ(entries.at(index).metrics.at("TPCBlocked"), blocked)
+ << "(expected at " << location.ToString() << ")";
+ EXPECT_EQ(entries.at(index).metrics.at("TPCBlockedInSettings"),
+ settings_blocked)
+ << "(expected at " << location.ToString() << ")";
+ }
+
+ // Navigates to a page with an iframe, then navigates the iframe to the given
+ // GURL. Can also set TPC blocking cookie.
+ void NavigateToURLAndIFrame(base::StringPiece host, const GURL iframe_url) {
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(
+ browser(), https_server()->GetURL(host, "/iframe.html")));
+ ASSERT_TRUE(NavigateIframeToURL(
+ browser()->tab_strip_model()->GetActiveWebContents(), "test",
+ iframe_url));
+ }
+
+ private:
+ net::EmbeddedTestServer https_server_;
+};
+
+// Verify that Metric only registers HTTP errors
+IN_PROC_BROWSER_TEST_F(HTTPErrProcBrowserTest, NoErr) {
+ ukm::TestAutoSetUkmRecorder ukm_recorder;
+
+ SetThirdPartyCookieBlocking(false);
+ NavigateToURLAndIFrame(
+ /*host=*/kHostA,
+ /*iframe_url=*/https_server()->GetURL(kHostA, "/title1.html"));
+ EXPECT_EQ(ukm_recorder
+ .GetEntries("ThirdPartyCookies.BreakageIndicator",
+ {"BreakageIndicatorType", "TPCBlocked",
+ "TPCBlockedInSettings"})
+ .size(),
+ 0u);
+}
+
+// Check that the ThirdPartyCookieBreakageIndicator UKM is sent on HTTP Error
+// without cookies blocked
+IN_PROC_BROWSER_TEST_F(HTTPErrProcBrowserTest, WithCookiesWithErr) {
+ ukm::TestAutoSetUkmRecorder ukm_recorder;
+
+ SetThirdPartyCookieBlocking(false);
+ NavigateToURLAndIFrame(
+ /*host=*/kHostA,
+ /*iframe_url=*/https_server()->GetURL(kHostB, "/page404.html"));
+ CheckServerErrBreakageMetrics(
+ /*ukm_recorder=*/ukm_recorder,
+ /*size=*/1,
+ /*index=*/0,
+ /*blocked=*/false,
+ /*settings_blocked=*/false);
+}
+
+// Check that the ThirdPartyCookieBreakageIndicator UKM is sent on HTTP Error
+// with cookies blocked in settings
+IN_PROC_BROWSER_TEST_F(HTTPErrProcBrowserTest, TPCBlockedInSettings4xxErr) {
+ ukm::TestAutoSetUkmRecorder ukm_recorder;
+
+ SetThirdPartyCookieBlocking(true);
+ NavigateToURLAndIFrame(
+ /*host=*/kHostA,
+ /*iframe_url=*/https_server()->GetURL(kHostB, "/page404.html"));
+ CheckServerErrBreakageMetrics(
+ /*ukm_recorder=*/ukm_recorder,
+ /*size=*/1,
+ /*index=*/0,
+ /*blocked=*/false,
+ /*settings_blocked=*/true);
+}
+
+// Check that the ThirdPartyCookieBreakageIndicator UKM works with image
+// subresources
+IN_PROC_BROWSER_TEST_F(HTTPErrProcBrowserTest, TPCBlockedImageErr) {
+ ukm::TestAutoSetUkmRecorder ukm_recorder;
+
+ SetThirdPartyCookieBlocking(true);
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(
+ browser(), https_server()->GetURL(kHostA, "/no_real_image.html")));
+ CheckServerErrBreakageMetrics(
+ /*ukm_recorder=*/ukm_recorder,
+ /*size=*/1,
+ /*index=*/0,
+ /*blocked=*/false,
+ /*settings_blocked=*/true);
+}
+
+// Check that the ThirdPartyCookieBreakageIndicator UKM works with fetches
+IN_PROC_BROWSER_TEST_F(HTTPErrProcBrowserTest, TPCBlockedFetchErr) {
+ ukm::TestAutoSetUkmRecorder ukm_recorder;
+
+ SetThirdPartyCookieBlocking(true);
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(
+ browser(), https_server()->GetURL(kHostA, "/no_real_fetch.html")));
+ CheckServerErrBreakageMetrics(
+ /*ukm_recorder=*/ukm_recorder,
+ /*size=*/1,
+ /*index=*/0,
+ /*blocked=*/false,
+ /*settings_blocked=*/true);
+}
+
+// Check that ThirdPartyCookieBreakageIndicator UKM works with subresource loads
+// within iframes
+IN_PROC_BROWSER_TEST_F(HTTPErrProcBrowserTest, TPCBlockedIframeErr) {
+ ukm::TestAutoSetUkmRecorder ukm_recorder;
+
+ SetThirdPartyCookieBlocking(true);
+ content::SetCookie(browser()->profile(),
+ https_server()->GetURL(kHostB, "/no_real_image.html"),
+ "thirdparty=1;SameSite=None;Secure");
+ NavigateToURLAndIFrame(
+ /*host=*/kHostA,
+ /*iframe_url=*/https_server()->GetURL(kHostB, "/no_real_image.html"));
+ CheckServerErrBreakageMetrics(
+ /*ukm_recorder=*/ukm_recorder,
+ /*size=*/1,
+ /*index=*/0,
+ /*blocked=*/true,
+ /*settings_blocked=*/true);
+}
+
+// Check that the ThirdPartyCookieBreakageIndicator UKM is sent on Server Error.
+IN_PROC_BROWSER_TEST_F(HTTPErrProcBrowserTest, TPCBlocked5xxErr) {
+ ukm::TestAutoSetUkmRecorder ukm_recorder;
+
+ SetThirdPartyCookieBlocking(true);
+ content::SetCookie(
+ browser()->profile(),
+ https_server()->GetURL(kHostB, "/echo-cookie-with-status?status=500"),
+ "thirdparty=1;SameSite=None;Secure");
+ NavigateToURLAndIFrame(
+ /*host=*/kHostA,
+ /*iframe_url=*/
+ https_server()->GetURL(kHostB, "/echo-cookie-with-status?status=500"));
+ CheckServerErrBreakageMetrics(
+ /*ukm_recorder=*/ukm_recorder,
+ /*size=*/1,
+ /*index=*/0,
+ /*blocked=*/true,
+ /*settings_blocked=*/true);
+}
+
+// Verify that ThirdPartyCookieBreakageIndicator UKM has correct value
+// when TPC are blocked in settings and by site
+IN_PROC_BROWSER_TEST_F(HTTPErrProcBrowserTest, TPCBlocked4xxErr) {
+ ukm::TestAutoSetUkmRecorder ukm_recorder;
+
+ SetThirdPartyCookieBlocking(true);
+ content::SetCookie(browser()->profile(),
+ https_server()->GetURL(kHostB, "/not-real.html"),
+ "thirdparty=1;SameSite=None;Secure");
+ NavigateToURLAndIFrame(
+ /*host=*/kHostA,
+ /*iframe_url=*/https_server()->GetURL(kHostB, "/not-real.html"));
+
+ CheckServerErrBreakageMetrics(
+ /*ukm_recorder=*/ukm_recorder,
+ /*size=*/1,
+ /*index=*/0,
+ /*blocked=*/true,
+ /*settings_blocked=*/true);
+}
+
+// Check that multiple entries are entered correctly
+IN_PROC_BROWSER_TEST_F(HTTPErrProcBrowserTest, MultiErrs) {
+ ukm::TestAutoSetUkmRecorder ukm_recorder;
+
+ SetThirdPartyCookieBlocking(true);
+ content::SetCookie(browser()->profile(),
+ https_server()->GetURL(kHostB, "/page404.html"),
+ "thirdparty=1;SameSite=None;Secure");
+ NavigateToURLAndIFrame(
+ /*host=*/kHostA,
+ /*iframe_url=*/https_server()->GetURL(kHostB, "/page404.html"));
+ CheckServerErrBreakageMetrics(
+ /*ukm_recorder=*/ukm_recorder,
+ /*size=*/1,
+ /*index=*/0,
+ /*blocked=*/true,
+ /*settings_blocked=*/true);
+
+ SetThirdPartyCookieBlocking(false);
+ NavigateToURLAndIFrame(
+ /*host=*/kHostA,
+ /*iframe_url=*/https_server()->GetURL(kHostA, "/not-real.html"));
+ CheckServerErrBreakageMetrics(
+ /*ukm_recorder=*/ukm_recorder,
+ /*size=*/2,
+ /*index=*/1,
+ /*blocked=*/false,
+ /*settings_blocked=*/false);
+}
diff --git a/chrome/browser/ui/tab_helpers.cc b/chrome/browser/ui/tab_helpers.cc
index 20d0576..31cdb67 100644
--- a/chrome/browser/ui/tab_helpers.cc
+++ b/chrome/browser/ui/tab_helpers.cc
@@ -83,6 +83,7 @@
#include "chrome/browser/sync/sessions/sync_sessions_web_contents_router_factory.h"
#include "chrome/browser/tab_contents/navigation_metrics_recorder.h"
#include "chrome/browser/tpcd/heuristics/opener_heuristic_tab_helper.h"
+#include "chrome/browser/tpcd/http_error_observer/http_error_tab_helper.h"
#include "chrome/browser/tpcd/metadata/devtools_observer.h"
#include "chrome/browser/translate/chrome_translate_client.h"
#include "chrome/browser/trusted_vault/trusted_vault_encryption_keys_tab_helper.h"
@@ -499,6 +500,7 @@
StorageAccessAPITabHelper::CreateForWebContents(
web_contents, StorageAccessAPIServiceFactory::GetForBrowserContext(
web_contents->GetBrowserContext()));
+ HttpErrorTabHelper::CreateForWebContents(web_contents);
sync_sessions::SyncSessionsRouterTabHelper::CreateForWebContents(
web_contents,
sync_sessions::SyncSessionsWebContentsRouterFactory::GetForProfile(
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 86fec30..35b4407 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1638,6 +1638,7 @@
"//chrome/browser/storage_access_api",
"//chrome/browser/top_level_storage_access_api:permissions",
"//chrome/browser/tpcd/heuristics:browser_tests",
+ "//chrome/browser/tpcd/http_error_observer:browser_tests",
"//chrome/browser/ui/color:color_headers",
"//chrome/browser/ui/side_panel:side_panel_enums",
"//chrome/browser/ui/tabs:tab_enums",
diff --git a/chrome/test/data/no_real_fetch.html b/chrome/test/data/no_real_fetch.html
new file mode 100644
index 0000000..334da09
--- /dev/null
+++ b/chrome/test/data/no_real_fetch.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test base with non-existent image file</title>
+<body>
+ <script>
+ fetch('notARealPage.html')
+ </script>
+</body>
\ No newline at end of file
diff --git a/chrome/test/data/no_real_image.html b/chrome/test/data/no_real_image.html
new file mode 100644
index 0000000..70dd3c8
--- /dev/null
+++ b/chrome/test/data/no_real_image.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test base with non-existent image file</title>
+<body>
+ <h1>Hello have a look at this "picture"!</h1>
+ <img src="not_a_real_image.png">
+</body>
\ No newline at end of file
diff --git a/net/cookies/cookie_util.h b/net/cookies/cookie_util.h
index 8fc03dc..e2e9ea3e 100644
--- a/net/cookies/cookie_util.h
+++ b/net/cookies/cookie_util.h
@@ -56,7 +56,8 @@
// at the end.
enum class BreakageIndicatorType {
USER_RELOAD = 0,
- kMaxValue = USER_RELOAD,
+ HTTP_ERROR = 1,
+ kMaxValue = HTTP_ERROR,
};
// Helper to fire telemetry indicating if a given request for storage was
// allowed or not by the provided |result|.
diff --git a/third_party/blink/public/mojom/loader/resource_load_info.mojom b/third_party/blink/public/mojom/loader/resource_load_info.mojom
index ce58a63..4eadb05 100644
--- a/third_party/blink/public/mojom/loader/resource_load_info.mojom
+++ b/third_party/blink/public/mojom/loader/resource_load_info.mojom
@@ -131,4 +131,7 @@
// The list of redirects that led to this resource load. Empty if there were
// no redirects.
array<RedirectInfo> redirect_info_chain;
+
+ // The HTTP Status Code
+ int32 http_status_code;
};
diff --git a/third_party/blink/renderer/platform/exported/resource_load_info_notifier_wrapper.cc b/third_party/blink/renderer/platform/exported/resource_load_info_notifier_wrapper.cc
index e584d9b..677b60c 100644
--- a/third_party/blink/renderer/platform/exported/resource_load_info_notifier_wrapper.cc
+++ b/third_party/blink/renderer/platform/exported/resource_load_info_notifier_wrapper.cc
@@ -125,6 +125,10 @@
network_utils::AlwaysAccessNetwork(response_head->headers);
resource_load_info_->network_info->remote_endpoint =
response_head->remote_endpoint;
+ if (response_head->headers) {
+ resource_load_info_->http_status_code =
+ response_head->headers->response_code();
+ }
if (task_runner_->BelongsToCurrentThread()) {
if (weak_wrapper_resource_load_info_notifier_) {
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index a4ff7ba..2763e9c 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -13998,6 +13998,7 @@
<enum name="BreakageIndicatorType">
<int value="0" label="User Reload"/>
+ <int value="1" label="HTTP Error"/>
</enum>
<enum name="BreakoutBoxUsage">