From c22027eae36c4a5a4a375d8c493f3ef1366d5558 Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Tue, 5 Oct 2021 12:35:32 -0700 Subject: [PATCH 1/3] chore: cherry-pick 39090918efac from chromium --- patches/chromium/.patches | 1 + .../chromium/cherry-pick-39090918efac.patch | 456 ++++++++++++++++++ 2 files changed, 457 insertions(+) create mode 100644 patches/chromium/cherry-pick-39090918efac.patch diff --git a/patches/chromium/.patches b/patches/chromium/.patches index f647cb9d31ac3..f92c15e5ed3aa 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -104,3 +104,4 @@ don_t_run_pcscan_notifythreadcreated_if_pcscan_is_disabled.patch logging_win32_only_create_a_console_if_logging_to_stderr.patch feat_expose_raw_response_headers_from_urlloader.patch fix_media_key_usage_with_globalshortcuts.patch +cherry-pick-39090918efac.patch diff --git a/patches/chromium/cherry-pick-39090918efac.patch b/patches/chromium/cherry-pick-39090918efac.patch new file mode 100644 index 0000000000000..9f08e104b69f1 --- /dev/null +++ b/patches/chromium/cherry-pick-39090918efac.patch @@ -0,0 +1,456 @@ +From 39090918efac313d376f65713f4de6a6ff0a55bb Mon Sep 17 00:00:00 2001 +From: cfredric +Date: Mon, 27 Sep 2021 22:14:18 +0000 +Subject: [PATCH] Consider HTTPS and WSS schemes identically for FPS. + +This modifies the FPS implementation to normalize wss:// URLs into +https:// URLs when determining the same-partiness of a request. + +This allows SameParty cookies to be sent on same-party WSS connection +requests. A browsertest is included to verify this. + +Bug: 1251688 +Change-Id: Id277288982805e0d29c6683e0c13d4b7c7cfe359 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3182786 +Reviewed-by: Maksim Orlovich +Reviewed-by: Shuran Huang +Commit-Queue: Chris Fredrickson +Cr-Commit-Position: refs/heads/main@{#925457} +--- + +diff --git a/chrome/browser/net/websocket_browsertest.cc b/chrome/browser/net/websocket_browsertest.cc +index 8a9fe6db..de48b97b 100644 +--- a/chrome/browser/net/websocket_browsertest.cc ++++ b/chrome/browser/net/websocket_browsertest.cc +@@ -21,6 +21,7 @@ + #include "base/test/bind.h" + #include "build/build_config.h" + #include "chrome/browser/chrome_notification_types.h" ++#include "chrome/browser/profiles/profile.h" + #include "chrome/browser/ui/browser.h" + #include "chrome/browser/ui/login/login_handler.h" + #include "chrome/browser/ui/login/login_handler_test_utils.h" +@@ -45,25 +46,31 @@ + #include "mojo/public/cpp/system/data_pipe.h" + #include "net/base/network_isolation_key.h" + #include "net/cookies/site_for_cookies.h" ++#include "net/dns/mock_host_resolver.h" + #include "net/test/embedded_test_server/embedded_test_server.h" + #include "net/test/spawned_test_server/spawned_test_server.h" + #include "net/test/test_data_directory.h" + #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" ++#include "services/network/public/cpp/network_switches.h" + #include "services/network/public/mojom/network_context.mojom.h" + #include "services/network/public/mojom/websocket.mojom.h" ++#include "testing/gmock/include/gmock/gmock.h" + #include "testing/gtest/include/gtest/gtest.h" + #include "url/gurl.h" + #include "url/origin.h" + + namespace { + ++using SSLOptions = net::SpawnedTestServer::SSLOptions; ++ + class WebSocketBrowserTest : public InProcessBrowserTest { + public: +- WebSocketBrowserTest() ++ explicit WebSocketBrowserTest( ++ SSLOptions::ServerCertificate cert = SSLOptions::CERT_OK) + : ws_server_(net::SpawnedTestServer::TYPE_WS, + net::GetWebSocketTestDataDirectory()), + wss_server_(net::SpawnedTestServer::TYPE_WSS, +- SSLOptions(SSLOptions::CERT_OK), ++ SSLOptions(cert), + net::GetWebSocketTestDataDirectory()) {} + + protected: +@@ -145,7 +152,6 @@ + net::SpawnedTestServer wss_server_; + + private: +- typedef net::SpawnedTestServer::SSLOptions SSLOptions; + std::unique_ptr watcher_; + + DISALLOW_COPY_AND_ASSIGN(WebSocketBrowserTest); +@@ -162,37 +168,72 @@ + }; + + // Framework for tests using the connect_to.html page served by a separate HTTP +-// server. ++// or HTTPS server. + class WebSocketBrowserConnectToTest : public WebSocketBrowserTest { + protected: +- WebSocketBrowserConnectToTest() { +- http_server_.ServeFilesFromSourceDirectory( +- net::GetWebSocketTestDataDirectory()); +- } ++ explicit WebSocketBrowserConnectToTest( ++ SSLOptions::ServerCertificate cert = SSLOptions::CERT_OK) ++ : WebSocketBrowserTest(cert) {} + + // The title watcher and HTTP server are set up automatically by the test + // framework. Each test case still needs to configure and start the + // WebSocket server(s) it needs. + void SetUpOnMainThread() override { ++ server().ServeFilesFromSourceDirectory( ++ net::GetWebSocketTestDataDirectory()); + WebSocketBrowserTest::SetUpOnMainThread(); +- ASSERT_TRUE(http_server_.Start()); ++ ASSERT_TRUE(server().Start()); + } + +- // Supply a ws: or wss: URL to connect to. +- void ConnectTo(GURL url) { +- ASSERT_TRUE(http_server_.Started()); ++ // Supply a ws: or wss: URL to connect to. Serves connect_to.html from the ++ // server's default host. ++ void ConnectTo(const GURL& url) { ++ ConnectTo(server().base_url().host(), url); ++ } ++ ++ // Supply a ws: or wss: URL to connect to via loading `host`/connect_to.html. ++ void ConnectTo(const std::string& host, const GURL& url) { ++ ASSERT_TRUE(server().Started()); + std::string query("url=" + url.spec()); + GURL::Replacements replacements; + replacements.SetQueryStr(query); + ASSERT_TRUE(ui_test_utils::NavigateToURL( +- browser(), http_server_.GetURL("/connect_to.html") ++ browser(), server() ++ .GetURL(host, "/connect_to.html") + .ReplaceComponents(replacements))); + } + +- private: ++ virtual net::EmbeddedTestServer& server() = 0; ++}; ++ ++// Concrete impl for tests that use connect_to.html over HTTP. ++class WebSocketBrowserHTTPConnectToTest : public WebSocketBrowserConnectToTest { ++ protected: ++ net::EmbeddedTestServer& server() override { return http_server_; } ++ + net::EmbeddedTestServer http_server_; + }; + ++// Concrete impl for tests that use connect_to.html over HTTPS. ++class WebSocketBrowserHTTPSConnectToTest ++ : public WebSocketBrowserConnectToTest { ++ protected: ++ explicit WebSocketBrowserHTTPSConnectToTest( ++ SSLOptions::ServerCertificate cert = SSLOptions::CERT_OK) ++ : WebSocketBrowserConnectToTest(cert), ++ https_server_(net::test_server::EmbeddedTestServer::TYPE_HTTPS) {} ++ ++ void SetUpOnMainThread() override { ++ host_resolver()->AddRule("*", "127.0.0.1"); ++ server().SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES); ++ WebSocketBrowserConnectToTest::SetUpOnMainThread(); ++ } ++ ++ net::EmbeddedTestServer& server() override { return https_server_; } ++ ++ net::EmbeddedTestServer https_server_; ++}; ++ + // Automatically fill in any login prompts that appear with the supplied + // credentials. + class AutoLogin : public content::NotificationObserver { +@@ -352,7 +393,7 @@ + EXPECT_EQ("PASS", WaitAndGetTitle()); + } + +-IN_PROC_BROWSER_TEST_F(WebSocketBrowserConnectToTest, ++IN_PROC_BROWSER_TEST_F(WebSocketBrowserHTTPConnectToTest, + WebSocketBasicAuthInWSURL) { + // Launch a basic-auth-protected WebSocket server. + ws_server_.set_websocket_basic_auth(true); +@@ -364,7 +405,7 @@ + EXPECT_EQ("PASS", WaitAndGetTitle()); + } + +-IN_PROC_BROWSER_TEST_F(WebSocketBrowserConnectToTest, ++IN_PROC_BROWSER_TEST_F(WebSocketBrowserHTTPConnectToTest, + WebSocketBasicAuthInWSURLBadCreds) { + // Launch a basic-auth-protected WebSocket server. + ws_server_.set_websocket_basic_auth(true); +@@ -376,7 +417,7 @@ + EXPECT_EQ("FAIL", WaitAndGetTitle()); + } + +-IN_PROC_BROWSER_TEST_F(WebSocketBrowserConnectToTest, ++IN_PROC_BROWSER_TEST_F(WebSocketBrowserHTTPConnectToTest, + WebSocketBasicAuthNoCreds) { + // Launch a basic-auth-protected WebSocket server. + ws_server_.set_websocket_basic_auth(true); +@@ -420,8 +461,7 @@ + https_server.ServeFilesFromSourceDirectory(GetChromeTestDataDir()); + net::SpawnedTestServer wss_server( + net::SpawnedTestServer::TYPE_WSS, +- net::SpawnedTestServer::SSLOptions( +- net::SpawnedTestServer::SSLOptions::CERT_COMMON_NAME_IS_DOMAIN), ++ SSLOptions(SSLOptions::CERT_COMMON_NAME_IS_DOMAIN), + net::GetWebSocketTestDataDirectory()); + // This test sets HSTS on localhost. To avoid being redirected to https, start + // the http server on 127.0.0.1 instead. +@@ -711,4 +751,43 @@ + EXPECT_EQ("FILE", WaitAndGetTitle()); + } + ++// A test fixture that enables First-Party Sets. ++class FirstPartySetsWebSocketBrowserTest ++ : public WebSocketBrowserHTTPSConnectToTest { ++ public: ++ FirstPartySetsWebSocketBrowserTest() ++ : WebSocketBrowserHTTPSConnectToTest(SSLOptions::CERT_TEST_NAMES) {} ++ ++ void SetUpCommandLine(base::CommandLine* command_line) override { ++ WebSocketBrowserTest::SetUpCommandLine(command_line); ++ command_line->AppendSwitchASCII( ++ network::switches::kUseFirstPartySet, ++ "https://a.test,https://b.test,https://c.test"); ++ } ++}; ++ ++IN_PROC_BROWSER_TEST_F(FirstPartySetsWebSocketBrowserTest, ++ SendsSamePartyCookies) { ++ ASSERT_TRUE(wss_server_.Start()); ++ ++ ASSERT_TRUE(content::SetCookie(browser()->profile(), ++ server().GetURL("a.test", "/"), ++ "same-party-cookie=1; SameParty; Secure")); ++ ASSERT_TRUE(content::SetCookie(browser()->profile(), ++ server().GetURL("a.test", "/"), ++ "same-site-cookie=1; SameSite=Lax; Secure")); ++ ++ content::DOMMessageQueue message_queue; ++ ConnectTo("b.test", wss_server_.GetURL("a.test", "echo-request-headers")); ++ ++ std::string message; ++ EXPECT_TRUE(message_queue.WaitForMessage(&message)); ++ // Only the SameParty cookie should have been sent, since it was a cross-site ++ // but same-party connection. ++ EXPECT_THAT(message, testing::HasSubstr("same-party-cookie=1")); ++ EXPECT_THAT(message, testing::Not(testing::HasSubstr("same-site-cookie=1"))); ++ ++ EXPECT_EQ("PASS", WaitAndGetTitle()); ++} ++ + } // namespace +diff --git a/net/data/websocket/connect_to.html b/net/data/websocket/connect_to.html +index 05c653f..8a6d782 100644 +--- a/net/data/websocket/connect_to.html ++++ b/net/data/websocket/connect_to.html +@@ -29,6 +29,17 @@ + document.title = 'FAIL'; + } + ++ws.onmessage = function(evt) ++{ ++ domAutomationController.send(evt.data); ++} ++ ++ws.onerror = function(evt) ++{ ++ console.error(`WebSocket error: '${evt.message}'`); ++} ++ ++ + + + +diff --git a/net/test/spawned_test_server/base_test_server.cc b/net/test/spawned_test_server/base_test_server.cc +index 70937f8..01359fa 100644 +--- a/net/test/spawned_test_server/base_test_server.cc ++++ b/net/test/spawned_test_server/base_test_server.cc +@@ -137,6 +137,8 @@ + case CERT_KEY_USAGE_RSA_DIGITAL_SIGNATURE: + return base::FilePath( + FILE_PATH_LITERAL("key_usage_rsa_digitalsignature.pem")); ++ case CERT_TEST_NAMES: ++ return base::FilePath(FILE_PATH_LITERAL("test_names.pem")); + default: + NOTREACHED(); + } +@@ -228,6 +230,14 @@ + return GURL(GetScheme() + "://" + host_port_pair_.ToString() + "/" + path); + } + ++GURL BaseTestServer::GetURL(const std::string& hostname, ++ const std::string& relative_url) const { ++ GURL local_url = GetURL(relative_url); ++ GURL::Replacements replace_host; ++ replace_host.SetHostStr(hostname); ++ return local_url.ReplaceComponents(replace_host); ++} ++ + GURL BaseTestServer::GetURLWithUser(const std::string& path, + const std::string& user) const { + return GURL(GetScheme() + "://" + user + "@" + host_port_pair_.ToString() + +diff --git a/net/test/spawned_test_server/base_test_server.h b/net/test/spawned_test_server/base_test_server.h +index 367ba3b..62b3234 100644 +--- a/net/test/spawned_test_server/base_test_server.h ++++ b/net/test/spawned_test_server/base_test_server.h +@@ -77,6 +77,11 @@ + // A certificate with invalid notBefore and notAfter times. Windows' + // certificate library will not parse this certificate. + CERT_BAD_VALIDITY, ++ ++ // A certificate that covers a number of test names. See [test_names] in ++ // net/data/ssl/scripts/ee.cnf. More may be added by editing this list and ++ // and rerunning net/data/ssl/scripts/generate-test-certs.sh. ++ CERT_TEST_NAMES, + }; + + // NOTE: the values of these enumerators are passed to the the Python test +@@ -198,6 +203,8 @@ + bool GetAddressList(AddressList* address_list) const WARN_UNUSED_RESULT; + + GURL GetURL(const std::string& path) const; ++ GURL GetURL(const std::string& hostname, ++ const std::string& relative_url) const; + + GURL GetURLWithUser(const std::string& path, + const std::string& user) const; +diff --git a/services/network/first_party_sets/first_party_sets.cc b/services/network/first_party_sets/first_party_sets.cc +index 1650c28d..826b403 100644 +--- a/services/network/first_party_sets/first_party_sets.cc ++++ b/services/network/first_party_sets/first_party_sets.cc +@@ -91,16 +91,17 @@ + const net::SchemefulSite* top_frame_site, + const std::set& party_context, + bool infer_singleton_sets) const { +- const net::SchemefulSite* site_owner = FindOwner(site, infer_singleton_sets); +- if (!site_owner) ++ const absl::optional site_owner = ++ FindOwner(site, infer_singleton_sets); ++ if (!site_owner.has_value()) + return false; + + const auto is_owned_by_site_owner = +- [this, site_owner, ++ [this, &site_owner, + infer_singleton_sets](const net::SchemefulSite& context_site) -> bool { +- const net::SchemefulSite* context_owner = ++ const absl::optional context_owner = + FindOwner(context_site, infer_singleton_sets); +- return context_owner && *context_owner == *site_owner; ++ return context_owner.has_value() && *context_owner == *site_owner; + }; + + if (top_frame_site && !is_owned_by_site_owner(*top_frame_site)) +@@ -131,7 +132,8 @@ + const absl::optional& top_frame_site, + const std::set& party_context) const { + constexpr bool infer_singleton_sets = true; +- const net::SchemefulSite* site_owner = FindOwner(site, infer_singleton_sets); ++ const absl::optional site_owner = ++ FindOwner(site, infer_singleton_sets); + // Note: the `party_context` consists of the intermediate frames (for frame + // requests) or intermediate frames and current frame for subresource + // requests. +@@ -152,18 +154,22 @@ + : net::FirstPartySetsContextType::kTopResourceMatchMixed; + } + +-const net::SchemefulSite* FirstPartySets::FindOwner( ++const absl::optional FirstPartySets::FindOwner( + const net::SchemefulSite& site, + bool infer_singleton_sets) const { +- const auto it = sets_.find(site); +- if (it == sets_.end()) +- return infer_singleton_sets ? &site : nullptr; +- return &it->second; ++ net::SchemefulSite normalized_site = site; ++ normalized_site.ConvertWebSocketToHttp(); ++ const auto it = sets_.find(normalized_site); ++ if (it != sets_.end()) ++ return it->second; ++ if (infer_singleton_sets) ++ return normalized_site; ++ return absl::nullopt; + } + + bool FirstPartySets::IsInNontrivialFirstPartySet( + const net::SchemefulSite& site) const { +- return base::Contains(sets_, site); ++ return FindOwner(site, /*infer_singleton_sets=*/false).has_value(); + } + + base::flat_map> +@@ -244,7 +250,8 @@ + for (const auto& old_pair : old_sets) { + const net::SchemefulSite& old_member = old_pair.first; + const net::SchemefulSite& old_owner = old_pair.second; +- const net::SchemefulSite* current_owner = FindOwner(old_member, false); ++ const absl::optional current_owner = ++ FindOwner(old_member, false); + // Look for the removed sites and the ones have owner changed. + if (!current_owner || *current_owner != old_owner) { + result.emplace(old_member); +diff --git a/services/network/first_party_sets/first_party_sets.h b/services/network/first_party_sets/first_party_sets.h +index 8158b55..fc87e51 100644 +--- a/services/network/first_party_sets/first_party_sets.h ++++ b/services/network/first_party_sets/first_party_sets.h +@@ -97,11 +97,12 @@ + base::OnceCallback callback); + + private: +- // Returns a pointer to `site`'s owner (optionally inferring a singleton set +- // if necessary), or `nullptr` if `site` has no owner. Must not return +- // `nullptr` if `infer_singleton_sets` is true. +- const net::SchemefulSite* FindOwner(const net::SchemefulSite& site, +- bool infer_singleton_sets) const; ++ // Returns `site`'s owner (optionally inferring a singleton set if necessary), ++ // or `nullopt` if `site` has no owner. Must not return `nullopt` if ++ // `infer_singleton_sets` is true. ++ const absl::optional FindOwner( ++ const net::SchemefulSite& site, ++ bool infer_singleton_sets) const; + + // We must ensure there's no intersection between the manually-specified set + // and the sets that came from Component Updater. (When reconciling the +diff --git a/services/network/first_party_sets/first_party_sets_unittest.cc b/services/network/first_party_sets/first_party_sets_unittest.cc +index 2055619..52eb8e8 100644 +--- a/services/network/first_party_sets/first_party_sets_unittest.cc ++++ b/services/network/first_party_sets/first_party_sets_unittest.cc +@@ -1167,6 +1167,8 @@ + net::SchemefulSite nonmember1(GURL("https://nonmember1.test")); + net::SchemefulSite member(GURL("https://member1.test")); + net::SchemefulSite owner(GURL("https://example.test")); ++ net::SchemefulSite wss_member(GURL("wss://member1.test")); ++ net::SchemefulSite wss_nonmember(GURL("wss://nonmember.test")); + + // Works as usual for sites that are in First-Party sets. + EXPECT_THAT(sets().ComputeContext(member, &member, {member}), +@@ -1180,10 +1182,17 @@ + EXPECT_THAT(sets().ComputeContext(member, &member, {member, owner}), + net::SamePartyContext(SamePartyContextType::kSameParty)); + ++ // Works if the site is provided with WSS scheme instead of HTTPS. ++ EXPECT_THAT(sets().ComputeContext(wss_member, &member, {member, owner}), ++ net::SamePartyContext(SamePartyContextType::kSameParty)); ++ + EXPECT_THAT(sets().ComputeContext(nonmember, &member, {member}), + net::SamePartyContext(SamePartyContextType::kCrossParty)); + EXPECT_THAT(sets().ComputeContext(member, &nonmember, {member}), + net::SamePartyContext(SamePartyContextType::kCrossParty)); ++ EXPECT_THAT( ++ sets().ComputeContext(wss_nonmember, &wss_member, {member, owner}), ++ net::SamePartyContext(SamePartyContextType::kCrossParty)); + + // Top&resource differs from Ancestors. + EXPECT_THAT(sets().ComputeContext(member, &member, {nonmember}), +@@ -1225,6 +1234,12 @@ + EXPECT_TRUE(sets().IsInNontrivialFirstPartySet( + net::SchemefulSite(GURL("https://member1.test")))); + ++ EXPECT_TRUE(sets().IsInNontrivialFirstPartySet( ++ net::SchemefulSite(GURL("wss://member1.test")))); ++ ++ EXPECT_FALSE(sets().IsInNontrivialFirstPartySet( ++ net::SchemefulSite(GURL("ws://member1.test")))); ++ + EXPECT_FALSE(sets().IsInNontrivialFirstPartySet( + net::SchemefulSite(GURL("https://nonmember.test")))); + } From 3a2d6426b613efc4bc3447e98a8c39ca30b8c2eb Mon Sep 17 00:00:00 2001 From: Keeley Hammond Date: Thu, 7 Oct 2021 16:40:19 -0700 Subject: [PATCH 2/3] chore: cherry-pick ec42dfd3545f from chromium --- patches/chromium/.patches | 1 + .../chromium/cherry-pick-ec42dfd3545f.patch | 627 ++++++++++++++++++ 2 files changed, 628 insertions(+) create mode 100644 patches/chromium/cherry-pick-ec42dfd3545f.patch diff --git a/patches/chromium/.patches b/patches/chromium/.patches index f92c15e5ed3aa..09cfbd2e521c1 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -105,3 +105,4 @@ logging_win32_only_create_a_console_if_logging_to_stderr.patch feat_expose_raw_response_headers_from_urlloader.patch fix_media_key_usage_with_globalshortcuts.patch cherry-pick-39090918efac.patch +cherry-pick-ec42dfd3545f.patch diff --git a/patches/chromium/cherry-pick-ec42dfd3545f.patch b/patches/chromium/cherry-pick-ec42dfd3545f.patch new file mode 100644 index 0000000000000..783f01498ceaf --- /dev/null +++ b/patches/chromium/cherry-pick-ec42dfd3545f.patch @@ -0,0 +1,627 @@ +From ec42dfd3545f1c40f643c90954368efbd7a4d0b4 Mon Sep 17 00:00:00 2001 +From: Shuran Huang +Date: Fri, 24 Sep 2021 00:47:47 +0000 +Subject: [PATCH] Add functions to pass in persisted FPSs and compute diffs. + +Pass the persisted FPSs and a callback that takes a FPSs into Network +Service. The persisted FPSs is parsed and compared to the current FPSs +in the FirstPartySets class, then call the callback with the current +FPSs. The function that passes in the persisted FPSs and the callback +has not been called anywhere yet. + +Bug: 1219656 +Change-Id: I08c531aa08d3aeeb772c1eb9a3a453a07b0349d3 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3103693 +Commit-Queue: Shuran Huang +Reviewed-by: Will Harris +Reviewed-by: Matt Menke +Reviewed-by: Chris Fredrickson +Cr-Commit-Position: refs/heads/main@{#924570} +--- + +diff --git a/services/network/first_party_sets/first_party_sets.cc b/services/network/first_party_sets/first_party_sets.cc +index f7e732e..1650c28d 100644 +--- a/services/network/first_party_sets/first_party_sets.cc ++++ b/services/network/first_party_sets/first_party_sets.cc +@@ -13,6 +13,7 @@ + #include "base/logging.h" + #include "base/ranges/algorithm.h" + #include "base/strings/string_split.h" ++#include "base/task/post_task.h" + #include "net/base/schemeful_site.h" + #include "net/cookies/cookie_constants.h" + #include "net/cookies/same_party_context.h" +@@ -72,12 +73,16 @@ + flag_value, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)); + + ApplyManuallySpecifiedSet(); ++ manual_sets_ready_ = true; ++ ClearSiteDataOnChangedSetsIfReady(); + } + + base::flat_map* + FirstPartySets::ParseAndSet(base::StringPiece raw_sets) { + sets_ = FirstPartySetParser::ParseSetsFromComponentUpdater(raw_sets); + ApplyManuallySpecifiedSet(); ++ component_sets_ready_ = true; ++ ClearSiteDataOnChangedSetsIfReady(); + return &sets_; + } + +@@ -218,4 +223,48 @@ + sets_.emplace(manual_owner, manual_owner); + } + ++void FirstPartySets::SetPersistedSets(base::StringPiece raw_sets) { ++ raw_persisted_sets_ = std::string(raw_sets); ++ persisted_sets_ready_ = true; ++ ClearSiteDataOnChangedSetsIfReady(); ++} ++ ++void FirstPartySets::SetOnSiteDataCleared( ++ base::OnceCallback callback) { ++ on_site_data_cleared_ = std::move(callback); ++ ClearSiteDataOnChangedSetsIfReady(); ++} ++ ++base::flat_set FirstPartySets::ComputeSetsDiff( ++ const base::flat_map& old_sets) { ++ if (old_sets.empty()) ++ return {}; ++ ++ base::flat_set result; ++ for (const auto& old_pair : old_sets) { ++ const net::SchemefulSite& old_member = old_pair.first; ++ const net::SchemefulSite& old_owner = old_pair.second; ++ const net::SchemefulSite* current_owner = FindOwner(old_member, false); ++ // Look for the removed sites and the ones have owner changed. ++ if (!current_owner || *current_owner != old_owner) { ++ result.emplace(old_member); ++ } ++ } ++ return result; ++} ++ ++void FirstPartySets::ClearSiteDataOnChangedSetsIfReady() { ++ if (!persisted_sets_ready_ || !component_sets_ready_ || !manual_sets_ready_ || ++ on_site_data_cleared_.is_null()) ++ return; ++ ++ base::flat_set diff = ComputeSetsDiff( ++ FirstPartySetParser::DeserializeFirstPartySets(raw_persisted_sets_)); ++ ++ // TODO(shuuran@chromium.org): Implement site state clearing. ++ ++ std::move(on_site_data_cleared_) ++ .Run(FirstPartySetParser::SerializeFirstPartySets(sets_)); ++} ++ + } // namespace network +diff --git a/services/network/first_party_sets/first_party_sets.h b/services/network/first_party_sets/first_party_sets.h +index 81e0e10..8158b55 100644 +--- a/services/network/first_party_sets/first_party_sets.h ++++ b/services/network/first_party_sets/first_party_sets.h +@@ -9,6 +9,7 @@ + #include + #include + ++#include "base/callback.h" + #include "base/containers/flat_map.h" + #include "base/containers/flat_set.h" + #include "net/base/schemeful_site.h" +@@ -87,6 +88,14 @@ + // the members of the set includes the owner. + base::flat_map> Sets() const; + ++ // Sets the `raw_persisted_sets_`, which is a JSON-encoded ++ // string representation of a map of site -> site. ++ void SetPersistedSets(base::StringPiece persisted_sets); ++ // Sets the `on_site_data_cleared_` callback, which takes input of a ++ // JSON-encoded string representation of a map of site -> site. ++ void SetOnSiteDataCleared( ++ base::OnceCallback callback); ++ + private: + // Returns a pointer to `site`'s owner (optionally inferring a singleton set + // if necessary), or `nullptr` if `site` has no owner. Must not return +@@ -101,6 +110,19 @@ + // `manually_specified_set_`. + void ApplyManuallySpecifiedSet(); + ++ // Compares the map `old_sets` to `sets_` and returns the set of sites that: ++ // 1) were in `old_sets` but are no longer in `sets_`, i.e. leave the FPSs; ++ // or, 2) mapped to a different owner site. ++ base::flat_set ComputeSetsDiff( ++ const base::flat_map& old_sets); ++ ++ // Checks the required inputs have been received, and if so, computes the diff ++ // between the `sets_` and the parsed `raw_persisted_sets_`, and clears the ++ // site data of the set of sites based on the diff. ++ // ++ // TODO(shuuran@chromium.org): Implement the code to clear site state. ++ void ClearSiteDataOnChangedSetsIfReady(); ++ + // Represents the mapping of site -> site, where keys are members of sets, and + // values are owners of the sets. Owners are explicitly represented as members + // of the set. +@@ -108,6 +130,22 @@ + absl::optional< + std::pair>> + manually_specified_set_; ++ ++ std::string raw_persisted_sets_; ++ ++ bool persisted_sets_ready_ = false; ++ bool component_sets_ready_ = false; ++ bool manual_sets_ready_ = false; ++ ++ // The callback runs after the site state clearing is completed. ++ base::OnceCallback on_site_data_cleared_; ++ ++ FRIEND_TEST_ALL_PREFIXES(FirstPartySets, ComputeSetsDiff_SitesJoined); ++ FRIEND_TEST_ALL_PREFIXES(FirstPartySets, ComputeSetsDiff_SitesLeft); ++ FRIEND_TEST_ALL_PREFIXES(FirstPartySets, ComputeSetsDiff_OwnerChanged); ++ FRIEND_TEST_ALL_PREFIXES(FirstPartySets, ComputeSetsDiff_OwnerLeft); ++ FRIEND_TEST_ALL_PREFIXES(FirstPartySets, ComputeSetsDiff_OwnerMemberRotate); ++ FRIEND_TEST_ALL_PREFIXES(FirstPartySets, ComputeSetsDiff_EmptySets); + }; + + } // namespace network +diff --git a/services/network/first_party_sets/first_party_sets_unittest.cc b/services/network/first_party_sets/first_party_sets_unittest.cc +index b929315..2055619 100644 +--- a/services/network/first_party_sets/first_party_sets_unittest.cc ++++ b/services/network/first_party_sets/first_party_sets_unittest.cc +@@ -7,6 +7,7 @@ + #include + + #include "base/json/json_reader.h" ++#include "base/test/bind.h" + #include "net/base/schemeful_site.h" + #include "net/cookies/cookie_constants.h" + #include "net/cookies/same_party_context.h" +@@ -204,6 +205,30 @@ + EXPECT_THAT(sets.ParseAndSet("[]"), Pointee(IsEmpty())); + } + ++TEST(FirstPartySets, SetsManuallySpecified_Valid_EmptyValue) { ++ FirstPartySets sets; ++ sets.SetManuallySpecifiedSet(""); ++ ++ // Set non-empty existing sets to distinguish the failure case from the no-op ++ // case when processing the manually-specified sets. ++ const std::string existing_sets = R"( ++ [ ++ { ++ "owner": "https://example.test", ++ "members": ["https://member.test"] ++ } ++ ] ++ )"; ++ ASSERT_TRUE(base::JSONReader::Read(existing_sets)); ++ ++ EXPECT_THAT(sets.ParseAndSet(existing_sets), ++ Pointee(UnorderedElementsAre( ++ Pair(SerializesTo("https://example.test"), ++ SerializesTo("https://example.test")), ++ Pair(SerializesTo("https://member.test"), ++ SerializesTo("https://example.test"))))); ++} ++ + TEST(FirstPartySets, SetsManuallySpecified_Valid_SingleMember) { + FirstPartySets sets; + sets.SetManuallySpecifiedSet("https://example.test,https://member.test"); +@@ -469,6 +494,311 @@ + SerializesTo("https://example.test"))))); + } + ++TEST(FirstPartySets, ComputeSetsDiff_SitesJoined) { ++ auto old_sets = base::flat_map{ ++ {net::SchemefulSite(GURL("https://example.test")), ++ net::SchemefulSite(GURL("https://example.test"))}, ++ {net::SchemefulSite(GURL("https://member1.test")), ++ net::SchemefulSite(GURL("https://example.test"))}, ++ {net::SchemefulSite(GURL("https://member3.test")), ++ net::SchemefulSite(GURL("https://example.test"))}}; ++ ++ // Consistency check the reviewer-friendly JSON format matches the input. ++ ASSERT_THAT(FirstPartySets().ParseAndSet(R"( ++ [ ++ { ++ "owner": "https://example.test", ++ "members": ["https://member1.test", "https://member3.test"] ++ } ++ ] ++ )"), ++ Pointee(old_sets)); ++ ++ FirstPartySets sets; ++ sets.ParseAndSet(R"( ++ [ ++ { ++ "owner": "https://example.test", ++ "members": ["https://member1.test", "https://member3.test"] ++ }, ++ { ++ "owner": "https://foo.test", ++ "members": ["https://member2.test"] ++ } ++ ] ++ )"); ++ // "https://foo.test" and "https://member2.test" joined FPSs. We don't clear ++ // site data upon joining, so the computed diff should be empty set. ++ EXPECT_THAT(sets.ComputeSetsDiff(old_sets), IsEmpty()); ++} ++ ++TEST(FirstPartySets, ComputeSetsDiff_SitesLeft) { ++ auto old_sets = base::flat_map{ ++ {net::SchemefulSite(GURL("https://example.test")), ++ net::SchemefulSite(GURL("https://example.test"))}, ++ {net::SchemefulSite(GURL("https://member1.test")), ++ net::SchemefulSite(GURL("https://example.test"))}, ++ {net::SchemefulSite(GURL("https://member3.test")), ++ net::SchemefulSite(GURL("https://example.test"))}, ++ {net::SchemefulSite(GURL("https://foo.test")), ++ net::SchemefulSite(GURL("https://foo.test"))}, ++ {net::SchemefulSite(GURL("https://member2.test")), ++ net::SchemefulSite(GURL("https://foo.test"))}}; ++ ++ // Consistency check the reviewer-friendly JSON format matches the input. ++ ASSERT_THAT(FirstPartySets().ParseAndSet(R"( ++ [ ++ { ++ "owner": "https://example.test", ++ "members": ["https://member1.test", "https://member3.test"] ++ }, ++ { ++ "owner": "https://foo.test", ++ "members": ["https://member2.test"] ++ }, ++ ] ++ )"), ++ Pointee(old_sets)); ++ ++ FirstPartySets sets; ++ sets.ParseAndSet(R"( ++ [ ++ { ++ "owner": "https://example.test", ++ "members": ["https://member1.test"] ++ }, ++ ] ++ )"); ++ // Expected diff: "https://foo.test", "https://member2.test" and ++ // "https://member3.test" left FPSs. ++ EXPECT_THAT(sets.ComputeSetsDiff(old_sets), ++ UnorderedElementsAre(SerializesTo("https://foo.test"), ++ SerializesTo("https://member2.test"), ++ SerializesTo("https://member3.test"))); ++} ++ ++TEST(FirstPartySets, ComputeSetsDiff_OwnerChanged) { ++ auto old_sets = base::flat_map{ ++ {net::SchemefulSite(GURL("https://example.test")), ++ net::SchemefulSite(GURL("https://example.test"))}, ++ {net::SchemefulSite(GURL("https://member1.test")), ++ net::SchemefulSite(GURL("https://example.test"))}, ++ {net::SchemefulSite(GURL("https://foo.test")), ++ net::SchemefulSite(GURL("https://foo.test"))}, ++ {net::SchemefulSite(GURL("https://member2.test")), ++ net::SchemefulSite(GURL("https://foo.test"))}, ++ {net::SchemefulSite(GURL("https://member3.test")), ++ net::SchemefulSite(GURL("https://foo.test"))}}; ++ ++ // Consistency check the reviewer-friendly JSON format matches the input. ++ ASSERT_THAT(FirstPartySets().ParseAndSet(R"( ++ [ ++ { ++ "owner": "https://example.test", ++ "members": ["https://member1.test"] ++ }, ++ { ++ "owner": "https://foo.test", ++ "members": ["https://member2.test", "https://member3.test"] ++ }, ++ ] ++ )"), ++ Pointee(old_sets)); ++ ++ FirstPartySets sets; ++ sets.ParseAndSet(R"( ++ [ ++ { ++ "owner": "https://example.test", ++ "members": ["https://member1.test", "https://member3.test"] ++ }, ++ { ++ "owner": "https://foo.test", ++ "members": ["https://member2.test"] ++ } ++ ] ++ )"); ++ // Expected diff: "https://member3.test" changed owner. ++ EXPECT_THAT(sets.ComputeSetsDiff(old_sets), ++ UnorderedElementsAre(SerializesTo("https://member3.test"))); ++} ++ ++TEST(FirstPartySets, ComputeSetsDiff_OwnerLeft) { ++ auto old_sets = base::flat_map{ ++ {net::SchemefulSite(GURL("https://example.test")), ++ net::SchemefulSite(GURL("https://example.test"))}, ++ {net::SchemefulSite(GURL("https://foo.test")), ++ net::SchemefulSite(GURL("https://example.test"))}, ++ {net::SchemefulSite(GURL("https://bar.test")), ++ net::SchemefulSite(GURL("https://example.test"))}}; ++ ++ // Consistency check the reviewer-friendly JSON format matches the input. ++ ASSERT_THAT(FirstPartySets().ParseAndSet(R"( ++ [ ++ { ++ "owner": "https://example.test", ++ "members": ["https://foo.test", "https://bar.test"] ++ } ++ ] ++ )"), ++ Pointee(old_sets)); ++ ++ FirstPartySets sets; ++ sets.ParseAndSet(R"( ++ [ ++ { ++ "owner": "https://foo.test", ++ "members": ["https://bar.test"] ++ } ++ ] ++ )"); ++ // Expected diff: "https://example.test" left FPSs, "https://foo.test" and ++ // "https://bar.test" changed owner. ++ // It would be valid to only have example.test in the diff, but our logic ++ // isn't sophisticated enough yet to know that foo.test and bar.test don't ++ // need to be included in the result. ++ EXPECT_THAT(sets.ComputeSetsDiff(old_sets), ++ UnorderedElementsAre(SerializesTo("https://example.test"), ++ SerializesTo("https://foo.test"), ++ SerializesTo("https://bar.test"))); ++} ++ ++TEST(FirstPartySets, ComputeSetsDiff_OwnerMemberRotate) { ++ auto old_sets = base::flat_map{ ++ {net::SchemefulSite(GURL("https://example.test")), ++ net::SchemefulSite(GURL("https://example.test"))}, ++ {net::SchemefulSite(GURL("https://foo.test")), ++ net::SchemefulSite(GURL("https://example.test"))}}; ++ ++ // Consistency check the reviewer-friendly JSON format matches the input. ++ ASSERT_THAT(FirstPartySets().ParseAndSet(R"( ++ [ ++ { ++ "owner": "https://example.test", ++ "members": ["https://foo.test"] ++ } ++ ] ++ )"), ++ Pointee(old_sets)); ++ ++ FirstPartySets sets; ++ sets.ParseAndSet(R"( ++ [ ++ { ++ "owner": "https://foo.test", ++ "members": ["https://example.test"] ++ } ++ ] ++ )"); ++ // Expected diff: "https://example.test" and "https://foo.test" changed owner. ++ // It would be valid to not include example.test and foo.test in the result, ++ // but our logic isn't sophisticated enough yet to know that.ß ++ EXPECT_THAT(sets.ComputeSetsDiff(old_sets), ++ UnorderedElementsAre(SerializesTo("https://example.test"), ++ SerializesTo("https://foo.test"))); ++} ++ ++TEST(FirstPartySets, ComputeSetsDiff_EmptySets) { ++ // Empty old_sets. ++ FirstPartySets sets; ++ sets.ParseAndSet(R"( ++ [ ++ { ++ "owner": "https://example.test", ++ "members": ["https://member1.test"] ++ }, ++ ] ++ )"); ++ EXPECT_THAT(sets.ComputeSetsDiff({}), IsEmpty()); ++ ++ // Empty current sets. ++ auto old_sets = base::flat_map{ ++ {net::SchemefulSite(GURL("https://example.test")), ++ net::SchemefulSite(GURL("https://example.test"))}, ++ {net::SchemefulSite(GURL("https://member1.test")), ++ net::SchemefulSite(GURL("https://example.test"))}}; ++ // Consistency check the reviewer-friendly JSON format matches the input. ++ ASSERT_THAT(FirstPartySets().ParseAndSet(R"( ++ [ ++ { ++ "owner": "https://example.test", ++ "members": ["https://member1.test"] ++ } ++ ] ++ )"), ++ Pointee(old_sets)); ++ EXPECT_THAT(FirstPartySets().ComputeSetsDiff(old_sets), ++ UnorderedElementsAre(SerializesTo("https://example.test"), ++ SerializesTo("https://member1.test"))); ++} ++ ++TEST(FirstPartySets, ClearSiteDataOnChangedSetsIfReady_NotReady) { ++ int callback_calls = 0; ++ auto callback = base::BindLambdaForTesting( ++ [&](const std::string& got) { callback_calls++; }); ++ // component sets not ready. ++ { ++ FirstPartySets sets; ++ callback_calls = 0; ++ sets.SetPersistedSets("{}"); ++ sets.SetManuallySpecifiedSet(""); ++ sets.SetOnSiteDataCleared(callback); ++ EXPECT_EQ(callback_calls, 0); ++ } ++ // manual sets not ready. ++ { ++ FirstPartySets sets; ++ callback_calls = 0; ++ sets.ParseAndSet("[]"); ++ sets.SetPersistedSets("{}"); ++ sets.SetOnSiteDataCleared(callback); ++ EXPECT_EQ(callback_calls, 0); ++ } ++ // persisted sets not ready. ++ { ++ FirstPartySets sets; ++ callback_calls = 0; ++ sets.ParseAndSet("[]"); ++ sets.SetManuallySpecifiedSet(""); ++ sets.SetOnSiteDataCleared(callback); ++ EXPECT_EQ(callback_calls, 0); ++ } ++ // callback not set. ++ { ++ FirstPartySets sets; ++ callback_calls = 0; ++ sets.ParseAndSet("[]"); ++ sets.SetManuallySpecifiedSet(""); ++ sets.SetPersistedSets("{}"); ++ EXPECT_EQ(callback_calls, 0); ++ } ++} ++ ++// The callback only runs when `old_sets` is generated and `sets` has merged the ++// inputs from Component Updater and command line flag. ++TEST(FirstPartySets, ClearSiteDataOnChangedSetsIfReady_Ready) { ++ FirstPartySets sets; ++ int callback_calls = 0; ++ sets.ParseAndSet(R"([ ++ { ++ "owner": "https://example.test", ++ "members": ["https://member1.test"] ++ } ++ ])"); ++ sets.SetManuallySpecifiedSet("https://example2.test,https://member2.test"); ++ sets.SetPersistedSets( ++ R"({"https://example.test":"https://example.test", ++ "https://member1.test":"https://example.test"})"); ++ sets.SetOnSiteDataCleared(base::BindLambdaForTesting([&](const std::string& ++ got) { ++ EXPECT_EQ( ++ got, ++ R"({"https://member1.test":"https://example.test","https://member2.test":"https://example2.test"})"); ++ callback_calls++; ++ })); ++ EXPECT_EQ(callback_calls, 1); ++} ++ + class FirstPartySetsTest : public ::testing::Test { + public: + FirstPartySetsTest() { +diff --git a/services/network/network_service.cc b/services/network/network_service.cc +index 5d598ff..c850d69 100644 +--- a/services/network/network_service.cc ++++ b/services/network/network_service.cc +@@ -343,8 +343,7 @@ + } + + first_party_sets_ = std::make_unique(); +- if (net::cookie_util::IsFirstPartySetsEnabled() && +- command_line->HasSwitch(switches::kUseFirstPartySet)) { ++ if (net::cookie_util::IsFirstPartySetsEnabled()) { + first_party_sets_->SetManuallySpecifiedSet( + command_line->GetSwitchValueASCII(switches::kUseFirstPartySet)); + } +@@ -785,6 +784,14 @@ + first_party_sets_->ParseAndSet(raw_sets); + } + ++void NetworkService::SetPersistedFirstPartySetsAndGetCurrentSets( ++ const std::string& persisted_sets, ++ mojom::NetworkService::SetPersistedFirstPartySetsAndGetCurrentSetsCallback ++ callback) { ++ first_party_sets_->SetPersistedSets(persisted_sets); ++ first_party_sets_->SetOnSiteDataCleared(std::move(callback)); ++} ++ + void NetworkService::SetExplicitlyAllowedPorts( + const std::vector& ports) { + net::SetExplicitlyAllowedPorts(ports); +diff --git a/services/network/network_service.h b/services/network/network_service.h +index e8c984a..a848746 100644 +--- a/services/network/network_service.h ++++ b/services/network/network_service.h +@@ -207,6 +207,10 @@ + void BindTestInterface( + mojo::PendingReceiver receiver) override; + void SetFirstPartySets(const std::string& raw_sets) override; ++ void SetPersistedFirstPartySetsAndGetCurrentSets( ++ const std::string& persisted_sets, ++ mojom::NetworkService::SetPersistedFirstPartySetsAndGetCurrentSetsCallback ++ callback) override; + void SetExplicitlyAllowedPorts(const std::vector& ports) override; + + // Returns an HttpAuthHandlerFactory for the given NetworkContext. +diff --git a/services/network/network_service_unittest.cc b/services/network/network_service_unittest.cc +index 5f23863..369509a 100644 +--- a/services/network/network_service_unittest.cc ++++ b/services/network/network_service_unittest.cc +@@ -930,6 +930,7 @@ + void SetUp() override { + test_server_.AddDefaultHandlers(base::FilePath(kServicesTestData)); + ASSERT_TRUE(test_server_.Start()); ++ scoped_features_.InitAndEnableFeature(net::features::kFirstPartySets); + service_ = NetworkService::CreateForTesting(); + service_->Bind(network_service_.BindNewPipeAndPassReceiver()); + } +@@ -993,6 +994,8 @@ + mojo::Remote network_service_; + mojo::Remote network_context_; + mojo::Remote loader_; ++ ++ base::test::ScopedFeatureList scoped_features_; + }; + + // Verifies that loading a URL through the network service's mojo interface +@@ -1172,6 +1175,18 @@ + run_loop.Run(); + } + ++TEST_F(NetworkServiceTestWithService, ++ SetPersistedFirstPartySetsAndGetCurrentSets) { ++ base::RunLoop run_loop; ++ network_service_->SetPersistedFirstPartySetsAndGetCurrentSets( ++ "", base::BindLambdaForTesting([&](const std::string& got) { ++ EXPECT_EQ(got, "{}"); ++ run_loop.Quit(); ++ })); ++ network_service_->SetFirstPartySets(""); ++ run_loop.Run(); ++} ++ + class TestNetworkChangeManagerClient + : public mojom::NetworkChangeManagerClient { + public: +diff --git a/services/network/public/mojom/network_service.mojom b/services/network/public/mojom/network_service.mojom +index fe5450b..59fbbde6 100644 +--- a/services/network/public/mojom/network_service.mojom ++++ b/services/network/public/mojom/network_service.mojom +@@ -373,6 +373,14 @@ + // cleared (except for the manually-specified set, if one exists). + SetFirstPartySets(string raw_sets); + ++ // Sets the First-Party Sets data that was persisted to compare it with the ++ // current First-Party Sets data set by `SetFirstPartySets()`, which is ++ // considered more up-to-date, and returns a serialized version of the current ++ // one. Both input and output are in format of the JSON-encoded string ++ // representation of a map of site -> site. ++ SetPersistedFirstPartySetsAndGetCurrentSets(string persisted_sets) ++ => (string up_to_date_sets); ++ + // Sets the list of ports which will be permitted even if they normally would + // be restricted. + SetExplicitlyAllowedPorts(array ports); From 194d85436603551015176ceb59fece039ea41ef0 Mon Sep 17 00:00:00 2001 From: VerteDinde Date: Thu, 7 Oct 2021 18:53:10 -0700 Subject: [PATCH 3/3] chore: reconcile FPS patch differences --- patches/chromium/.patches | 2 +- .../chromium/cherry-pick-39090918efac.patch | 73 ++++++++++--------- .../chromium/cherry-pick-ec42dfd3545f.patch | 52 +++++++------ 3 files changed, 63 insertions(+), 64 deletions(-) diff --git a/patches/chromium/.patches b/patches/chromium/.patches index 09cfbd2e521c1..1c23eb660d1b5 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -104,5 +104,5 @@ don_t_run_pcscan_notifythreadcreated_if_pcscan_is_disabled.patch logging_win32_only_create_a_console_if_logging_to_stderr.patch feat_expose_raw_response_headers_from_urlloader.patch fix_media_key_usage_with_globalshortcuts.patch -cherry-pick-39090918efac.patch cherry-pick-ec42dfd3545f.patch +cherry-pick-39090918efac.patch diff --git a/patches/chromium/cherry-pick-39090918efac.patch b/patches/chromium/cherry-pick-39090918efac.patch index 9f08e104b69f1..93c258432be49 100644 --- a/patches/chromium/cherry-pick-39090918efac.patch +++ b/patches/chromium/cherry-pick-39090918efac.patch @@ -1,7 +1,7 @@ -From 39090918efac313d376f65713f4de6a6ff0a55bb Mon Sep 17 00:00:00 2001 +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: cfredric Date: Mon, 27 Sep 2021 22:14:18 +0000 -Subject: [PATCH] Consider HTTPS and WSS schemes identically for FPS. +Subject: Consider HTTPS and WSS schemes identically for FPS. This modifies the FPS implementation to normalize wss:// URLs into https:// URLs when determining the same-partiness of a request. @@ -16,10 +16,9 @@ Reviewed-by: Maksim Orlovich Reviewed-by: Shuran Huang Commit-Queue: Chris Fredrickson Cr-Commit-Position: refs/heads/main@{#925457} ---- diff --git a/chrome/browser/net/websocket_browsertest.cc b/chrome/browser/net/websocket_browsertest.cc -index 8a9fe6db..de48b97b 100644 +index 0714f0d0231d677edd0f0cdf82f4129ddc43a5c2..6f2f101743fbd470bafe90d7e5d14351ee0ff708 100644 --- a/chrome/browser/net/websocket_browsertest.cc +++ b/chrome/browser/net/websocket_browsertest.cc @@ -21,6 +21,7 @@ @@ -64,7 +63,7 @@ index 8a9fe6db..de48b97b 100644 net::GetWebSocketTestDataDirectory()) {} protected: -@@ -145,7 +152,6 @@ +@@ -145,7 +152,6 @@ class WebSocketBrowserTest : public InProcessBrowserTest { net::SpawnedTestServer wss_server_; private: @@ -72,7 +71,7 @@ index 8a9fe6db..de48b97b 100644 std::unique_ptr watcher_; DISALLOW_COPY_AND_ASSIGN(WebSocketBrowserTest); -@@ -162,37 +168,72 @@ +@@ -162,37 +168,72 @@ class WebSocketBrowserTestWithAllowFileAccessFromFiles }; // Framework for tests using the connect_to.html page served by a separate HTTP @@ -114,11 +113,13 @@ index 8a9fe6db..de48b97b 100644 std::string query("url=" + url.spec()); GURL::Replacements replacements; replacements.SetQueryStr(query); - ASSERT_TRUE(ui_test_utils::NavigateToURL( -- browser(), http_server_.GetURL("/connect_to.html") +- ui_test_utils::NavigateToURL(browser(), +- http_server_.GetURL("/connect_to.html") +- .ReplaceComponents(replacements)); ++ ASSERT_TRUE(ui_test_utils::NavigateToURL( + browser(), server() + .GetURL(host, "/connect_to.html") - .ReplaceComponents(replacements))); ++ .ReplaceComponents(replacements))); } - private: @@ -156,7 +157,7 @@ index 8a9fe6db..de48b97b 100644 // Automatically fill in any login prompts that appear with the supplied // credentials. class AutoLogin : public content::NotificationObserver { -@@ -352,7 +393,7 @@ +@@ -352,7 +393,7 @@ IN_PROC_BROWSER_TEST_F(WebSocketBrowserTest, EXPECT_EQ("PASS", WaitAndGetTitle()); } @@ -165,7 +166,7 @@ index 8a9fe6db..de48b97b 100644 WebSocketBasicAuthInWSURL) { // Launch a basic-auth-protected WebSocket server. ws_server_.set_websocket_basic_auth(true); -@@ -364,7 +405,7 @@ +@@ -364,7 +405,7 @@ IN_PROC_BROWSER_TEST_F(WebSocketBrowserConnectToTest, EXPECT_EQ("PASS", WaitAndGetTitle()); } @@ -174,7 +175,7 @@ index 8a9fe6db..de48b97b 100644 WebSocketBasicAuthInWSURLBadCreds) { // Launch a basic-auth-protected WebSocket server. ws_server_.set_websocket_basic_auth(true); -@@ -376,7 +417,7 @@ +@@ -376,7 +417,7 @@ IN_PROC_BROWSER_TEST_F(WebSocketBrowserConnectToTest, EXPECT_EQ("FAIL", WaitAndGetTitle()); } @@ -183,7 +184,7 @@ index 8a9fe6db..de48b97b 100644 WebSocketBasicAuthNoCreds) { // Launch a basic-auth-protected WebSocket server. ws_server_.set_websocket_basic_auth(true); -@@ -420,8 +461,7 @@ +@@ -420,8 +461,7 @@ IN_PROC_BROWSER_TEST_F(WebSocketBrowserTest, MAYBE_WebSocketAppliesHSTS) { https_server.ServeFilesFromSourceDirectory(GetChromeTestDataDir()); net::SpawnedTestServer wss_server( net::SpawnedTestServer::TYPE_WSS, @@ -193,7 +194,7 @@ index 8a9fe6db..de48b97b 100644 net::GetWebSocketTestDataDirectory()); // This test sets HSTS on localhost. To avoid being redirected to https, start // the http server on 127.0.0.1 instead. -@@ -711,4 +751,43 @@ +@@ -711,4 +751,43 @@ IN_PROC_BROWSER_TEST_F(WebSocketBrowserTestWithAllowFileAccessFromFiles, EXPECT_EQ("FILE", WaitAndGetTitle()); } @@ -238,10 +239,10 @@ index 8a9fe6db..de48b97b 100644 + } // namespace diff --git a/net/data/websocket/connect_to.html b/net/data/websocket/connect_to.html -index 05c653f..8a6d782 100644 +index 05c653fc5d2ab9a333efea5b4c5eee83a03bbe07..8a6d78214fe5974cbb0ec62b61f4d7fdcdf42c3b 100644 --- a/net/data/websocket/connect_to.html +++ b/net/data/websocket/connect_to.html -@@ -29,6 +29,17 @@ +@@ -29,6 +29,17 @@ ws.onclose = function() document.title = 'FAIL'; } @@ -260,19 +261,19 @@ index 05c653f..8a6d782 100644 diff --git a/net/test/spawned_test_server/base_test_server.cc b/net/test/spawned_test_server/base_test_server.cc -index 70937f8..01359fa 100644 +index 9caaf0ad501322f480be9867909e2e6cb8c56503..54c84e4d0bbd51640b374532fd92903b2e01de58 100644 --- a/net/test/spawned_test_server/base_test_server.cc +++ b/net/test/spawned_test_server/base_test_server.cc -@@ -137,6 +137,8 @@ - case CERT_KEY_USAGE_RSA_DIGITAL_SIGNATURE: - return base::FilePath( +@@ -156,6 +156,8 @@ base::FilePath BaseTestServer::SSLOptions::GetCertificateFile() const { FILE_PATH_LITERAL("key_usage_rsa_digitalsignature.pem")); + case CERT_AUTO: + return base::FilePath(); + case CERT_TEST_NAMES: + return base::FilePath(FILE_PATH_LITERAL("test_names.pem")); default: NOTREACHED(); } -@@ -228,6 +230,14 @@ +@@ -249,6 +251,14 @@ GURL BaseTestServer::GetURL(const std::string& path) const { return GURL(GetScheme() + "://" + host_port_pair_.ToString() + "/" + path); } @@ -288,10 +289,10 @@ index 70937f8..01359fa 100644 const std::string& user) const { return GURL(GetScheme() + "://" + user + "@" + host_port_pair_.ToString() + diff --git a/net/test/spawned_test_server/base_test_server.h b/net/test/spawned_test_server/base_test_server.h -index 367ba3b..62b3234 100644 +index 6c209afcdeeed129ec58f4c55a78501d707fd8f3..848698160b6eba1a02618bfaa968114d10776395 100644 --- a/net/test/spawned_test_server/base_test_server.h +++ b/net/test/spawned_test_server/base_test_server.h -@@ -77,6 +77,11 @@ +@@ -82,6 +82,11 @@ class BaseTestServer { // A certificate with invalid notBefore and notAfter times. Windows' // certificate library will not parse this certificate. CERT_BAD_VALIDITY, @@ -302,8 +303,8 @@ index 367ba3b..62b3234 100644 + CERT_TEST_NAMES, }; - // NOTE: the values of these enumerators are passed to the the Python test -@@ -198,6 +203,8 @@ + // Bitmask of key exchange algorithms that the test server supports and that +@@ -277,6 +282,8 @@ class BaseTestServer { bool GetAddressList(AddressList* address_list) const WARN_UNUSED_RESULT; GURL GetURL(const std::string& path) const; @@ -313,10 +314,10 @@ index 367ba3b..62b3234 100644 GURL GetURLWithUser(const std::string& path, const std::string& user) const; diff --git a/services/network/first_party_sets/first_party_sets.cc b/services/network/first_party_sets/first_party_sets.cc -index 1650c28d..826b403 100644 +index 1650c28d8b6c61b30531e1e2ef3e2869d8450360..826b403a2a9702c255ee9b7ea38dc74b9e18791d 100644 --- a/services/network/first_party_sets/first_party_sets.cc +++ b/services/network/first_party_sets/first_party_sets.cc -@@ -91,16 +91,17 @@ +@@ -91,16 +91,17 @@ bool FirstPartySets::IsContextSamePartyWithSite( const net::SchemefulSite* top_frame_site, const std::set& party_context, bool infer_singleton_sets) const { @@ -339,7 +340,7 @@ index 1650c28d..826b403 100644 }; if (top_frame_site && !is_owned_by_site_owner(*top_frame_site)) -@@ -131,7 +132,8 @@ +@@ -131,7 +132,8 @@ net::FirstPartySetsContextType FirstPartySets::ComputeContextType( const absl::optional& top_frame_site, const std::set& party_context) const { constexpr bool infer_singleton_sets = true; @@ -349,7 +350,7 @@ index 1650c28d..826b403 100644 // Note: the `party_context` consists of the intermediate frames (for frame // requests) or intermediate frames and current frame for subresource // requests. -@@ -152,18 +154,22 @@ +@@ -152,18 +154,22 @@ net::FirstPartySetsContextType FirstPartySets::ComputeContextType( : net::FirstPartySetsContextType::kTopResourceMatchMixed; } @@ -378,7 +379,7 @@ index 1650c28d..826b403 100644 } base::flat_map> -@@ -244,7 +250,8 @@ +@@ -244,7 +250,8 @@ base::flat_set FirstPartySets::ComputeSetsDiff( for (const auto& old_pair : old_sets) { const net::SchemefulSite& old_member = old_pair.first; const net::SchemefulSite& old_owner = old_pair.second; @@ -389,10 +390,10 @@ index 1650c28d..826b403 100644 if (!current_owner || *current_owner != old_owner) { result.emplace(old_member); diff --git a/services/network/first_party_sets/first_party_sets.h b/services/network/first_party_sets/first_party_sets.h -index 8158b55..fc87e51 100644 +index 8158b555856526170051cba72a08312a5528de51..fc87e5155667befc5a94bbe539ca71dc47d5f7d1 100644 --- a/services/network/first_party_sets/first_party_sets.h +++ b/services/network/first_party_sets/first_party_sets.h -@@ -97,11 +97,12 @@ +@@ -97,11 +97,12 @@ class FirstPartySets { base::OnceCallback callback); private: @@ -411,10 +412,10 @@ index 8158b55..fc87e51 100644 // We must ensure there's no intersection between the manually-specified set // and the sets that came from Component Updater. (When reconciling the diff --git a/services/network/first_party_sets/first_party_sets_unittest.cc b/services/network/first_party_sets/first_party_sets_unittest.cc -index 2055619..52eb8e8 100644 +index 2055619f4c999cbfd5a5ee4780e2eb5c1dad5816..52eb8e8a3d87172353c64bba972311db2889c693 100644 --- a/services/network/first_party_sets/first_party_sets_unittest.cc +++ b/services/network/first_party_sets/first_party_sets_unittest.cc -@@ -1167,6 +1167,8 @@ +@@ -1167,6 +1167,8 @@ TEST_F(FirstPartySetsTest, ComputeContext) { net::SchemefulSite nonmember1(GURL("https://nonmember1.test")); net::SchemefulSite member(GURL("https://member1.test")); net::SchemefulSite owner(GURL("https://example.test")); @@ -423,7 +424,7 @@ index 2055619..52eb8e8 100644 // Works as usual for sites that are in First-Party sets. EXPECT_THAT(sets().ComputeContext(member, &member, {member}), -@@ -1180,10 +1182,17 @@ +@@ -1180,10 +1182,17 @@ TEST_F(FirstPartySetsTest, ComputeContext) { EXPECT_THAT(sets().ComputeContext(member, &member, {member, owner}), net::SamePartyContext(SamePartyContextType::kSameParty)); @@ -441,7 +442,7 @@ index 2055619..52eb8e8 100644 // Top&resource differs from Ancestors. EXPECT_THAT(sets().ComputeContext(member, &member, {nonmember}), -@@ -1225,6 +1234,12 @@ +@@ -1225,6 +1234,12 @@ TEST_F(FirstPartySetsTest, IsInNontrivialFirstPartySet) { EXPECT_TRUE(sets().IsInNontrivialFirstPartySet( net::SchemefulSite(GURL("https://member1.test")))); diff --git a/patches/chromium/cherry-pick-ec42dfd3545f.patch b/patches/chromium/cherry-pick-ec42dfd3545f.patch index 783f01498ceaf..df1d1c2d4c35e 100644 --- a/patches/chromium/cherry-pick-ec42dfd3545f.patch +++ b/patches/chromium/cherry-pick-ec42dfd3545f.patch @@ -1,7 +1,7 @@ -From ec42dfd3545f1c40f643c90954368efbd7a4d0b4 Mon Sep 17 00:00:00 2001 +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Shuran Huang Date: Fri, 24 Sep 2021 00:47:47 +0000 -Subject: [PATCH] Add functions to pass in persisted FPSs and compute diffs. +Subject: Add functions to pass in persisted FPSs and compute diffs. Pass the persisted FPSs and a callback that takes a FPSs into Network Service. The persisted FPSs is parsed and compared to the current FPSs @@ -17,10 +17,9 @@ Reviewed-by: Will Harris Reviewed-by: Matt Menke Reviewed-by: Chris Fredrickson Cr-Commit-Position: refs/heads/main@{#924570} ---- diff --git a/services/network/first_party_sets/first_party_sets.cc b/services/network/first_party_sets/first_party_sets.cc -index f7e732e..1650c28d 100644 +index f7e732e88d6e6ebc5daed9169d5eee336a9de8c1..1650c28d8b6c61b30531e1e2ef3e2869d8450360 100644 --- a/services/network/first_party_sets/first_party_sets.cc +++ b/services/network/first_party_sets/first_party_sets.cc @@ -13,6 +13,7 @@ @@ -31,7 +30,7 @@ index f7e732e..1650c28d 100644 #include "net/base/schemeful_site.h" #include "net/cookies/cookie_constants.h" #include "net/cookies/same_party_context.h" -@@ -72,12 +73,16 @@ +@@ -72,12 +73,16 @@ void FirstPartySets::SetManuallySpecifiedSet(const std::string& flag_value) { flag_value, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)); ApplyManuallySpecifiedSet(); @@ -48,7 +47,7 @@ index f7e732e..1650c28d 100644 return &sets_; } -@@ -218,4 +223,48 @@ +@@ -218,4 +223,48 @@ void FirstPartySets::ApplyManuallySpecifiedSet() { sets_.emplace(manual_owner, manual_owner); } @@ -98,7 +97,7 @@ index f7e732e..1650c28d 100644 + } // namespace network diff --git a/services/network/first_party_sets/first_party_sets.h b/services/network/first_party_sets/first_party_sets.h -index 81e0e10..8158b55 100644 +index 81e0e1080d965947a2ebc1635638c25ad75a1bf7..8158b555856526170051cba72a08312a5528de51 100644 --- a/services/network/first_party_sets/first_party_sets.h +++ b/services/network/first_party_sets/first_party_sets.h @@ -9,6 +9,7 @@ @@ -109,7 +108,7 @@ index 81e0e10..8158b55 100644 #include "base/containers/flat_map.h" #include "base/containers/flat_set.h" #include "net/base/schemeful_site.h" -@@ -87,6 +88,14 @@ +@@ -87,6 +88,14 @@ class FirstPartySets { // the members of the set includes the owner. base::flat_map> Sets() const; @@ -124,7 +123,7 @@ index 81e0e10..8158b55 100644 private: // Returns a pointer to `site`'s owner (optionally inferring a singleton set // if necessary), or `nullptr` if `site` has no owner. Must not return -@@ -101,6 +110,19 @@ +@@ -101,6 +110,19 @@ class FirstPartySets { // `manually_specified_set_`. void ApplyManuallySpecifiedSet(); @@ -144,7 +143,7 @@ index 81e0e10..8158b55 100644 // Represents the mapping of site -> site, where keys are members of sets, and // values are owners of the sets. Owners are explicitly represented as members // of the set. -@@ -108,6 +130,22 @@ +@@ -108,6 +130,22 @@ class FirstPartySets { absl::optional< std::pair>> manually_specified_set_; @@ -168,7 +167,7 @@ index 81e0e10..8158b55 100644 } // namespace network diff --git a/services/network/first_party_sets/first_party_sets_unittest.cc b/services/network/first_party_sets/first_party_sets_unittest.cc -index b929315..2055619 100644 +index b929315d9b857e0f86d1d726f7cefefb7ad8e54c..2055619f4c999cbfd5a5ee4780e2eb5c1dad5816 100644 --- a/services/network/first_party_sets/first_party_sets_unittest.cc +++ b/services/network/first_party_sets/first_party_sets_unittest.cc @@ -7,6 +7,7 @@ @@ -179,7 +178,7 @@ index b929315..2055619 100644 #include "net/base/schemeful_site.h" #include "net/cookies/cookie_constants.h" #include "net/cookies/same_party_context.h" -@@ -204,6 +205,30 @@ +@@ -204,6 +205,30 @@ TEST(FirstPartySets, SetsManuallySpecified_Invalid_RegisteredDomain_Member) { EXPECT_THAT(sets.ParseAndSet("[]"), Pointee(IsEmpty())); } @@ -210,7 +209,7 @@ index b929315..2055619 100644 TEST(FirstPartySets, SetsManuallySpecified_Valid_SingleMember) { FirstPartySets sets; sets.SetManuallySpecifiedSet("https://example.test,https://member.test"); -@@ -469,6 +494,311 @@ +@@ -469,6 +494,311 @@ TEST(FirstPartySets, SetsManuallySpecified_PrunesInducedSingletons) { SerializesTo("https://example.test"))))); } @@ -523,10 +522,10 @@ index b929315..2055619 100644 public: FirstPartySetsTest() { diff --git a/services/network/network_service.cc b/services/network/network_service.cc -index 5d598ff..c850d69 100644 +index 3f34171edf0c96df3b08de77e4e2be5b26eeca67..975eedaff9c38d0ee20d0fe617cc6391ea083d70 100644 --- a/services/network/network_service.cc +++ b/services/network/network_service.cc -@@ -343,8 +343,7 @@ +@@ -343,8 +343,7 @@ void NetworkService::Initialize(mojom::NetworkServiceParamsPtr params, } first_party_sets_ = std::make_unique(); @@ -536,7 +535,7 @@ index 5d598ff..c850d69 100644 first_party_sets_->SetManuallySpecifiedSet( command_line->GetSwitchValueASCII(switches::kUseFirstPartySet)); } -@@ -785,6 +784,14 @@ +@@ -785,6 +784,14 @@ void NetworkService::SetFirstPartySets(const std::string& raw_sets) { first_party_sets_->ParseAndSet(raw_sets); } @@ -552,10 +551,10 @@ index 5d598ff..c850d69 100644 const std::vector& ports) { net::SetExplicitlyAllowedPorts(ports); diff --git a/services/network/network_service.h b/services/network/network_service.h -index e8c984a..a848746 100644 +index 1da4505fc9fe478e00353cd55e615878ea875aa0..963e22f6d5e957684dc56dd6e3ae31fa430a355e 100644 --- a/services/network/network_service.h +++ b/services/network/network_service.h -@@ -207,6 +207,10 @@ +@@ -204,6 +204,10 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkService void BindTestInterface( mojo::PendingReceiver receiver) override; void SetFirstPartySets(const std::string& raw_sets) override; @@ -567,10 +566,10 @@ index e8c984a..a848746 100644 // Returns an HttpAuthHandlerFactory for the given NetworkContext. diff --git a/services/network/network_service_unittest.cc b/services/network/network_service_unittest.cc -index 5f23863..369509a 100644 +index 7c8cbbbba3dd2084095d91b9195d69c335809263..6d1fdfd9b236c7129548fe626143636cfdb56bd5 100644 --- a/services/network/network_service_unittest.cc +++ b/services/network/network_service_unittest.cc -@@ -930,6 +930,7 @@ +@@ -927,6 +927,7 @@ class NetworkServiceTestWithService : public testing::Test { void SetUp() override { test_server_.AddDefaultHandlers(base::FilePath(kServicesTestData)); ASSERT_TRUE(test_server_.Start()); @@ -578,16 +577,15 @@ index 5f23863..369509a 100644 service_ = NetworkService::CreateForTesting(); service_->Bind(network_service_.BindNewPipeAndPassReceiver()); } -@@ -993,6 +994,8 @@ - mojo::Remote network_service_; - mojo::Remote network_context_; +@@ -992,6 +993,7 @@ class NetworkServiceTestWithService : public testing::Test { mojo::Remote loader_; -+ + + DISALLOW_COPY_AND_ASSIGN(NetworkServiceTestWithService); + base::test::ScopedFeatureList scoped_features_; }; // Verifies that loading a URL through the network service's mojo interface -@@ -1172,6 +1175,18 @@ +@@ -1171,6 +1173,18 @@ TEST_F(NetworkServiceTestWithService, GetNetworkList) { run_loop.Run(); } @@ -607,10 +605,10 @@ index 5f23863..369509a 100644 : public mojom::NetworkChangeManagerClient { public: diff --git a/services/network/public/mojom/network_service.mojom b/services/network/public/mojom/network_service.mojom -index fe5450b..59fbbde6 100644 +index fe5450b20b3c4a8490e853dd236bf6baefa90b81..59fbbde6ffc30d51304a72f402eee7c664ea111b 100644 --- a/services/network/public/mojom/network_service.mojom +++ b/services/network/public/mojom/network_service.mojom -@@ -373,6 +373,14 @@ +@@ -373,6 +373,14 @@ interface NetworkService { // cleared (except for the manually-specified set, if one exists). SetFirstPartySets(string raw_sets);