// Copyright 2018 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef NET_COOKIES_COOKIE_STORE_CHANGE_UNITTEST_H #define NET_COOKIES_COOKIE_STORE_CHANGE_UNITTEST_H #include "base/bind.h" #include "net/cookies/canonical_cookie.h" #include "net/cookies/cookie_change_dispatcher_test_helpers.h" #include "net/cookies/cookie_constants.h" #include "net/cookies/cookie_store.h" #include "net/cookies/cookie_store_unittest.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" namespace net { namespace { // Used to sort CookieChanges when testing stores without exact change ordering. // // The ordering relation must match the order in which the tests below issue // cookie calls. Changes to this method should be tested by running the tests // below with CookieMonsterTestTraits::has_exact_change_ordering set to both // true and false. bool CookieChangeLessThan(const CookieChangeInfo& lhs, const CookieChangeInfo& rhs) { if (lhs.cookie.Name() != rhs.cookie.Name()) return lhs.cookie.Name() < rhs.cookie.Name(); if (lhs.cookie.Value() != rhs.cookie.Value()) return lhs.cookie.Value() < rhs.cookie.Value(); if (lhs.cookie.Domain() != rhs.cookie.Domain()) return lhs.cookie.Domain() < rhs.cookie.Domain(); return lhs.cause < rhs.cause; } } // namespace // Google Test supports at most 50 tests per typed case, so the tests here are // broken up into multiple cases. template class CookieStoreChangeTestBase : public CookieStoreTest { protected: using CookieStoreTest::FindAndDeleteCookie; // Drains all pending tasks on the run loop(s) involved in the test. void DeliverChangeNotifications() { CookieStoreTestTraits::DeliverChangeNotifications(); } bool FindAndDeleteCookie(CookieStore* cs, const std::string& domain, const std::string& name, const std::string& path) { for (auto& cookie : this->GetAllCookies(cs)) { if (cookie.Domain() == domain && cookie.Name() == name && cookie.Path() == path) { return this->DeleteCanonicalCookie(cs, cookie); } } return false; } // Could be static, but it's actually easier to have it be a member function. ::testing::AssertionResult MatchesCause(CookieChangeCause expected_cause, CookieChangeCause actual_cause) { if (!CookieChangeCauseIsDeletion(expected_cause) || CookieStoreTestTraits::has_exact_change_cause) { if (expected_cause == actual_cause) return ::testing::AssertionSuccess(); return ::testing::AssertionFailure() << "expected " << expected_cause << " got " << actual_cause; } if (CookieChangeCauseIsDeletion(actual_cause)) return ::testing::AssertionSuccess(); return ::testing::AssertionFailure() << "expected a deletion cause, got " << actual_cause; } bool IsExpectedAccessSemantics(net::CookieAccessSemantics expected_semantics, net::CookieAccessSemantics actual_semantics) { if (CookieStoreTestTraits::supports_cookie_access_semantics) return expected_semantics == actual_semantics; return actual_semantics == net::CookieAccessSemantics::UNKNOWN; } static void OnCookieChange(std::vector* changes, const CookieChangeInfo& notification) { if (CookieStoreTestTraits::has_exact_change_ordering) { changes->push_back(notification); } else { // Assumes the vector is sorted before the insertion. If true, the vector // will remain sorted. changes->insert(std::upper_bound(changes->begin(), changes->end(), notification, CookieChangeLessThan), notification); } } }; template class CookieStoreChangeGlobalTest : public CookieStoreChangeTestBase {}; TYPED_TEST_SUITE_P(CookieStoreChangeGlobalTest); template class CookieStoreChangeUrlTest : public CookieStoreChangeTestBase {}; TYPED_TEST_SUITE_P(CookieStoreChangeUrlTest); template class CookieStoreChangeNamedTest : public CookieStoreChangeTestBase {}; TYPED_TEST_SUITE_P(CookieStoreChangeNamedTest); TYPED_TEST_P(CookieStoreChangeGlobalTest, NoCookie) { if (!TypeParam::supports_global_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForAllChanges(base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); this->DeliverChangeNotifications(); EXPECT_EQ(0u, cookie_changes.size()); } TYPED_TEST_P(CookieStoreChangeGlobalTest, InitialCookie) { if (!TypeParam::supports_global_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes; this->SetCookie(cs, this->http_www_foo_.url(), "A=B"); this->DeliverChangeNotifications(); std::unique_ptr subscription( cs->GetChangeDispatcher().AddCallbackForAllChanges(base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes)))); this->DeliverChangeNotifications(); EXPECT_EQ(0u, cookie_changes.size()); } TYPED_TEST_P(CookieStoreChangeGlobalTest, InsertOne) { if (!TypeParam::supports_global_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForAllChanges(base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes.size()); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B")); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes.size()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::INSERTED, cookie_changes[0].cause)); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].cookie.Domain()); EXPECT_EQ("A", cookie_changes[0].cookie.Name()); EXPECT_EQ("B", cookie_changes[0].cookie.Value()); } TYPED_TEST_P(CookieStoreChangeGlobalTest, InsertMany) { if (!TypeParam::supports_global_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForAllChanges(base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B")); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "C=D")); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "E=F")); EXPECT_TRUE(this->SetCookie(cs, this->http_bar_com_.url(), "G=H")); this->DeliverChangeNotifications(); // Check that the cookie changes are dispatched before calling GetCookies. // This is not an ASSERT because the following expectations produce useful // debugging information if they fail. EXPECT_EQ(4u, cookie_changes.size()); EXPECT_EQ("A=B; C=D; E=F", this->GetCookies(cs, this->http_www_foo_.url())); EXPECT_EQ("G=H", this->GetCookies(cs, this->http_bar_com_.url())); ASSERT_LE(1u, cookie_changes.size()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::INSERTED, cookie_changes[0].cause)); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].cookie.Domain()); EXPECT_EQ("A", cookie_changes[0].cookie.Name()); EXPECT_EQ("B", cookie_changes[0].cookie.Value()); ASSERT_LE(2u, cookie_changes.size()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[1].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::INSERTED, cookie_changes[1].cause)); EXPECT_EQ("C", cookie_changes[1].cookie.Name()); EXPECT_EQ("D", cookie_changes[1].cookie.Value()); ASSERT_LE(3u, cookie_changes.size()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[2].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::INSERTED, cookie_changes[2].cause)); EXPECT_EQ("E", cookie_changes[2].cookie.Name()); EXPECT_EQ("F", cookie_changes[2].cookie.Value()); ASSERT_LE(4u, cookie_changes.size()); EXPECT_EQ(this->http_bar_com_.url().host(), cookie_changes[3].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::INSERTED, cookie_changes[3].cause)); EXPECT_EQ("G", cookie_changes[3].cookie.Name()); EXPECT_EQ("H", cookie_changes[3].cookie.Value()); EXPECT_EQ(4u, cookie_changes.size()); } TYPED_TEST_P(CookieStoreChangeGlobalTest, DeleteOne) { if (!TypeParam::supports_global_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForAllChanges(base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B")); this->DeliverChangeNotifications(); EXPECT_EQ(1u, cookie_changes.size()); cookie_changes.clear(); EXPECT_TRUE( this->FindAndDeleteCookie(cs, this->http_www_foo_.url().host(), "A")); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes.size()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::EXPLICIT, cookie_changes[0].cause)); EXPECT_EQ("A", cookie_changes[0].cookie.Name()); EXPECT_EQ("B", cookie_changes[0].cookie.Value()); } TYPED_TEST_P(CookieStoreChangeGlobalTest, DeleteTwo) { if (!TypeParam::supports_global_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForAllChanges(base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B")); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "C=D")); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "E=F")); EXPECT_TRUE(this->SetCookie(cs, this->http_bar_com_.url(), "G=H")); this->DeliverChangeNotifications(); EXPECT_EQ(4u, cookie_changes.size()); cookie_changes.clear(); EXPECT_TRUE( this->FindAndDeleteCookie(cs, this->http_www_foo_.url().host(), "C")); EXPECT_TRUE( this->FindAndDeleteCookie(cs, this->http_bar_com_.url().host(), "G")); this->DeliverChangeNotifications(); // Check that the cookie changes are dispatched before calling GetCookies. // This is not an ASSERT because the following expectations produce useful // debugging information if they fail. EXPECT_EQ(2u, cookie_changes.size()); EXPECT_EQ("A=B; E=F", this->GetCookies(cs, this->http_www_foo_.url())); EXPECT_EQ("", this->GetCookies(cs, this->http_bar_com_.url())); ASSERT_LE(1u, cookie_changes.size()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::EXPLICIT, cookie_changes[0].cause)); EXPECT_EQ("C", cookie_changes[0].cookie.Name()); EXPECT_EQ("D", cookie_changes[0].cookie.Value()); ASSERT_EQ(2u, cookie_changes.size()); EXPECT_EQ(this->http_bar_com_.url().host(), cookie_changes[1].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::EXPLICIT, cookie_changes[1].cause)); EXPECT_EQ("G", cookie_changes[1].cookie.Name()); EXPECT_EQ("H", cookie_changes[1].cookie.Value()); } TYPED_TEST_P(CookieStoreChangeGlobalTest, Overwrite) { if (!TypeParam::supports_global_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForAllChanges(base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes.size()); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B")); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes.size()); cookie_changes.clear(); // Replacing an existing cookie is actually a two-phase delete + set // operation, so we get an extra notification. EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=C")); this->DeliverChangeNotifications(); ASSERT_LE(1u, cookie_changes.size()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].cookie.Domain()); EXPECT_TRUE(this->MatchesCause(CookieChangeCause::OVERWRITE, cookie_changes[0].cause)); EXPECT_EQ("A", cookie_changes[0].cookie.Name()); EXPECT_EQ("B", cookie_changes[0].cookie.Value()); ASSERT_LE(2u, cookie_changes.size()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[1].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::INSERTED, cookie_changes[1].cause)); EXPECT_EQ("A", cookie_changes[1].cookie.Name()); EXPECT_EQ("C", cookie_changes[1].cookie.Value()); EXPECT_EQ(2u, cookie_changes.size()); } TYPED_TEST_P(CookieStoreChangeGlobalTest, OverwriteWithHttpOnly) { if (!TypeParam::supports_global_cookie_tracking) return; // Insert a cookie "A" for path "/path1" CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForAllChanges(base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes.size()); EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "A=B; path=/path1")); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes.size()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::INSERTED, cookie_changes[0].cause)); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].cookie.Domain()); EXPECT_EQ("A", cookie_changes[0].cookie.Name()); EXPECT_EQ("B", cookie_changes[0].cookie.Value()); EXPECT_FALSE(cookie_changes[0].cookie.IsHttpOnly()); cookie_changes.clear(); // Insert a cookie "A" for path "/path1", that is httponly. This should // overwrite the non-http-only version. CookieOptions allow_httponly; allow_httponly.set_include_httponly(); allow_httponly.set_same_site_cookie_context( net::CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT); EXPECT_TRUE(this->CreateAndSetCookie(cs, this->http_www_foo_.url(), "A=C; path=/path1; httponly", allow_httponly)); this->DeliverChangeNotifications(); ASSERT_LE(1u, cookie_changes.size()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].cookie.Domain()); EXPECT_TRUE(this->MatchesCause(CookieChangeCause::OVERWRITE, cookie_changes[0].cause)); EXPECT_EQ("A", cookie_changes[0].cookie.Name()); EXPECT_EQ("B", cookie_changes[0].cookie.Value()); EXPECT_FALSE(cookie_changes[0].cookie.IsHttpOnly()); ASSERT_LE(2u, cookie_changes.size()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[1].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::INSERTED, cookie_changes[1].cause)); EXPECT_EQ("A", cookie_changes[1].cookie.Name()); EXPECT_EQ("C", cookie_changes[1].cookie.Value()); EXPECT_TRUE(cookie_changes[1].cookie.IsHttpOnly()); EXPECT_EQ(2u, cookie_changes.size()); } TYPED_TEST_P(CookieStoreChangeGlobalTest, Deregister) { if (!TypeParam::supports_global_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForAllChanges(base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes.size()); // Insert a cookie and make sure it is seen. EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B")); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes.size()); EXPECT_EQ("A", cookie_changes[0].cookie.Name()); EXPECT_EQ("B", cookie_changes[0].cookie.Value()); cookie_changes.clear(); // De-register the subscription. subscription.reset(); // Insert a second cookie and make sure that it's not visible. EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "C=D")); this->DeliverChangeNotifications(); EXPECT_EQ(0u, cookie_changes.size()); } TYPED_TEST_P(CookieStoreChangeGlobalTest, DeregisterMultiple) { if (!TypeParam::supports_global_cookie_tracking || !TypeParam::supports_multiple_tracking_callbacks) return; CookieStore* cs = this->GetCookieStore(); // Register two subscriptions. std::vector cookie_changes_1; std::unique_ptr subscription1 = cs->GetChangeDispatcher().AddCallbackForAllChanges(base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_1))); std::vector cookie_changes_2; std::unique_ptr subscription2 = cs->GetChangeDispatcher().AddCallbackForAllChanges(base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_2))); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes_1.size()); ASSERT_EQ(0u, cookie_changes_2.size()); // Insert a cookie and make sure it's seen. EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B")); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes_1.size()); EXPECT_EQ("A", cookie_changes_1[0].cookie.Name()); EXPECT_EQ("B", cookie_changes_1[0].cookie.Value()); cookie_changes_1.clear(); ASSERT_EQ(1u, cookie_changes_2.size()); EXPECT_EQ("A", cookie_changes_2[0].cookie.Name()); EXPECT_EQ("B", cookie_changes_2[0].cookie.Value()); cookie_changes_2.clear(); // De-register the second subscription. subscription2.reset(); // Insert a second cookie and make sure that it's only visible in one // change array. EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "C=D")); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes_1.size()); EXPECT_EQ("C", cookie_changes_1[0].cookie.Name()); EXPECT_EQ("D", cookie_changes_1[0].cookie.Value()); cookie_changes_1.clear(); ASSERT_EQ(0u, cookie_changes_2.size()); } // Confirm that a listener does not receive notifications for changes that // happened right before the subscription was established. TYPED_TEST_P(CookieStoreChangeGlobalTest, DispatchRace) { if (!TypeParam::supports_global_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); // This cookie insertion should not be seen. EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B")); // DeliverChangeNotifications() must NOT be called before the subscription is // established. std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForAllChanges(base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "C=D")); this->DeliverChangeNotifications(); EXPECT_LE(1u, cookie_changes.size()); EXPECT_EQ("C", cookie_changes[0].cookie.Name()); EXPECT_EQ("D", cookie_changes[0].cookie.Value()); ASSERT_EQ(1u, cookie_changes.size()); } // Confirm that deregistering a subscription blocks the notification if the // deregistration happened after the change but before the notification was // received. TYPED_TEST_P(CookieStoreChangeGlobalTest, DeregisterRace) { if (!TypeParam::supports_global_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForAllChanges(base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes.size()); // Insert a cookie and make sure it's seen. EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B")); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes.size()); EXPECT_EQ("A", cookie_changes[0].cookie.Name()); EXPECT_EQ("B", cookie_changes[0].cookie.Value()); cookie_changes.clear(); // Insert a cookie, confirm it is not seen, deregister the subscription, run // until idle, and confirm the cookie is still not seen. EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "C=D")); // Note that by the API contract it's perfectly valid to have received the // notification immediately, i.e. synchronously with the cookie change. In // that case, there's nothing to test. if (1u == cookie_changes.size()) return; // A task was posted by the SetCookie() above, but has not yet arrived. If it // arrived before the subscription is destroyed, callback execution would be // valid. Destroy the subscription so as to lose the race and make sure the // task posted arrives after the subscription was destroyed. subscription.reset(); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes.size()); } TYPED_TEST_P(CookieStoreChangeGlobalTest, DeregisterRaceMultiple) { if (!TypeParam::supports_global_cookie_tracking || !TypeParam::supports_multiple_tracking_callbacks) return; CookieStore* cs = this->GetCookieStore(); // Register two subscriptions. std::vector cookie_changes_1, cookie_changes_2; std::unique_ptr subscription1 = cs->GetChangeDispatcher().AddCallbackForAllChanges(base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_1))); std::unique_ptr subscription2 = cs->GetChangeDispatcher().AddCallbackForAllChanges(base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_2))); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes_1.size()); ASSERT_EQ(0u, cookie_changes_2.size()); // Insert a cookie and make sure it's seen. EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B")); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes_1.size()); EXPECT_EQ("A", cookie_changes_1[0].cookie.Name()); EXPECT_EQ("B", cookie_changes_1[0].cookie.Value()); cookie_changes_1.clear(); ASSERT_EQ(1u, cookie_changes_2.size()); EXPECT_EQ("A", cookie_changes_2[0].cookie.Name()); EXPECT_EQ("B", cookie_changes_2[0].cookie.Value()); cookie_changes_2.clear(); // Insert a cookie, confirm it is not seen, deregister a subscription, run // until idle, and confirm the cookie is still not seen. EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "C=D")); // Note that by the API contract it's perfectly valid to have received the // notification immediately, i.e. synchronously with the cookie change. In // that case, there's nothing to test. if (1u == cookie_changes_2.size()) return; // A task was posted by the SetCookie() above, but has not yet arrived. If it // arrived before the subscription is destroyed, callback execution would be // valid. Destroy one of the subscriptions so as to lose the race and make // sure the task posted arrives after the subscription was destroyed. subscription2.reset(); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes_1.size()); EXPECT_EQ("C", cookie_changes_1[0].cookie.Name()); EXPECT_EQ("D", cookie_changes_1[0].cookie.Value()); // No late notification was received. ASSERT_EQ(0u, cookie_changes_2.size()); } TYPED_TEST_P(CookieStoreChangeGlobalTest, MultipleSubscriptions) { if (!TypeParam::supports_global_cookie_tracking || !TypeParam::supports_multiple_tracking_callbacks) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes_1, cookie_changes_2; std::unique_ptr subscription1 = cs->GetChangeDispatcher().AddCallbackForAllChanges(base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_1))); std::unique_ptr subscription2 = cs->GetChangeDispatcher().AddCallbackForAllChanges(base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_2))); this->DeliverChangeNotifications(); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B")); this->DeliverChangeNotifications(); ASSERT_EQ(1U, cookie_changes_1.size()); EXPECT_EQ("A", cookie_changes_1[0].cookie.Name()); EXPECT_EQ("B", cookie_changes_1[0].cookie.Value()); ASSERT_EQ(1U, cookie_changes_2.size()); EXPECT_EQ("A", cookie_changes_2[0].cookie.Name()); EXPECT_EQ("B", cookie_changes_2[0].cookie.Value()); } TYPED_TEST_P(CookieStoreChangeGlobalTest, ChangeIncludesCookieAccessSemantics) { if (!TypeParam::supports_global_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); // if !supports_cookie_access_semantics, the delegate will be stored but will // not be used. auto access_delegate = std::make_unique(); access_delegate->SetExpectationForCookieDomain("domain1.test", CookieAccessSemantics::LEGACY); access_delegate->SetExpectationForCookieDomain( "domain2.test", CookieAccessSemantics::NONLEGACY); access_delegate->SetExpectationForCookieDomain( "domain3.test", CookieAccessSemantics::UNKNOWN); cs->SetCookieAccessDelegate(std::move(access_delegate)); std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForAllChanges(base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); this->CreateAndSetCookie(cs, GURL("http://domain1.test"), "cookie=1", CookieOptions::MakeAllInclusive()); this->CreateAndSetCookie(cs, GURL("http://domain2.test"), "cookie=1", CookieOptions::MakeAllInclusive()); this->CreateAndSetCookie(cs, GURL("http://domain3.test"), "cookie=1", CookieOptions::MakeAllInclusive()); this->CreateAndSetCookie(cs, GURL("http://domain4.test"), "cookie=1", CookieOptions::MakeAllInclusive()); this->DeliverChangeNotifications(); ASSERT_EQ(4u, cookie_changes.size()); EXPECT_EQ("domain1.test", cookie_changes[0].cookie.Domain()); EXPECT_TRUE(this->IsExpectedAccessSemantics( CookieAccessSemantics::LEGACY, cookie_changes[0].access_semantics)); EXPECT_EQ("domain2.test", cookie_changes[1].cookie.Domain()); EXPECT_TRUE(this->IsExpectedAccessSemantics( CookieAccessSemantics::NONLEGACY, cookie_changes[1].access_semantics)); EXPECT_EQ("domain3.test", cookie_changes[2].cookie.Domain()); EXPECT_TRUE(this->IsExpectedAccessSemantics( CookieAccessSemantics::UNKNOWN, cookie_changes[2].access_semantics)); EXPECT_EQ("domain4.test", cookie_changes[3].cookie.Domain()); EXPECT_TRUE(this->IsExpectedAccessSemantics( CookieAccessSemantics::UNKNOWN, cookie_changes[3].access_semantics)); } TYPED_TEST_P(CookieStoreChangeUrlTest, NoCookie) { if (!TypeParam::supports_url_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForUrl( this->http_www_foo_.url(), base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); this->DeliverChangeNotifications(); EXPECT_EQ(0u, cookie_changes.size()); } TYPED_TEST_P(CookieStoreChangeUrlTest, InitialCookie) { if (!TypeParam::supports_url_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes; this->SetCookie(cs, this->http_www_foo_.url(), "A=B"); this->DeliverChangeNotifications(); std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForUrl( this->http_www_foo_.url(), base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); this->DeliverChangeNotifications(); EXPECT_EQ(0u, cookie_changes.size()); } TYPED_TEST_P(CookieStoreChangeUrlTest, InsertOne) { if (!TypeParam::supports_url_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForUrl( this->http_www_foo_.url(), base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes.size()); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B")); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes.size()); EXPECT_EQ("A", cookie_changes[0].cookie.Name()); EXPECT_EQ("B", cookie_changes[0].cookie.Value()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::INSERTED, cookie_changes[0].cause)); } TYPED_TEST_P(CookieStoreChangeUrlTest, InsertMany) { if (!TypeParam::supports_url_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForUrl( this->http_www_foo_.url(), base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B")); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "C=D")); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "E=F")); this->DeliverChangeNotifications(); ASSERT_LE(1u, cookie_changes.size()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::INSERTED, cookie_changes[0].cause)); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].cookie.Domain()); EXPECT_EQ("A", cookie_changes[0].cookie.Name()); EXPECT_EQ("B", cookie_changes[0].cookie.Value()); ASSERT_LE(2u, cookie_changes.size()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[1].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::INSERTED, cookie_changes[1].cause)); EXPECT_EQ("C", cookie_changes[1].cookie.Name()); EXPECT_EQ("D", cookie_changes[1].cookie.Value()); ASSERT_LE(3u, cookie_changes.size()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[2].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::INSERTED, cookie_changes[2].cause)); EXPECT_EQ("E", cookie_changes[2].cookie.Name()); EXPECT_EQ("F", cookie_changes[2].cookie.Value()); EXPECT_EQ(3u, cookie_changes.size()); } TYPED_TEST_P(CookieStoreChangeUrlTest, InsertFiltering) { if (!TypeParam::supports_url_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForUrl( this->www_foo_foo_.url(), base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes.size()); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B; path=/")); EXPECT_TRUE(this->SetCookie(cs, this->http_bar_com_.url(), "C=D; path=/")); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "E=F; path=/bar")); EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "G=H; path=/foo/bar")); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "I=J; path=/foo")); EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "K=L; domain=foo.com")); this->DeliverChangeNotifications(); ASSERT_LE(1u, cookie_changes.size()); EXPECT_EQ("A", cookie_changes[0].cookie.Name()); EXPECT_EQ("B", cookie_changes[0].cookie.Value()); EXPECT_EQ("/", cookie_changes[0].cookie.Path()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::INSERTED, cookie_changes[0].cause)); ASSERT_LE(2u, cookie_changes.size()); EXPECT_EQ("I", cookie_changes[1].cookie.Name()); EXPECT_EQ("J", cookie_changes[1].cookie.Value()); EXPECT_EQ("/foo", cookie_changes[1].cookie.Path()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[1].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::INSERTED, cookie_changes[1].cause)); ASSERT_LE(3u, cookie_changes.size()); EXPECT_EQ("K", cookie_changes[2].cookie.Name()); EXPECT_EQ("L", cookie_changes[2].cookie.Value()); EXPECT_EQ("/", cookie_changes[2].cookie.Path()); EXPECT_EQ(".foo.com", cookie_changes[2].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::INSERTED, cookie_changes[2].cause)); EXPECT_EQ(3u, cookie_changes.size()); } TYPED_TEST_P(CookieStoreChangeUrlTest, DeleteOne) { if (!TypeParam::supports_url_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForUrl( this->http_www_foo_.url(), base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B")); this->DeliverChangeNotifications(); EXPECT_EQ(1u, cookie_changes.size()); cookie_changes.clear(); EXPECT_TRUE( this->FindAndDeleteCookie(cs, this->http_www_foo_.url().host(), "A")); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes.size()); EXPECT_EQ("A", cookie_changes[0].cookie.Name()); EXPECT_EQ("B", cookie_changes[0].cookie.Value()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].cookie.Domain()); ASSERT_TRUE( this->MatchesCause(CookieChangeCause::EXPLICIT, cookie_changes[0].cause)); } TYPED_TEST_P(CookieStoreChangeUrlTest, DeleteTwo) { if (!TypeParam::supports_url_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForUrl( this->http_www_foo_.url(), base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B")); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "C=D")); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "E=F")); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "G=H")); this->DeliverChangeNotifications(); EXPECT_EQ(4u, cookie_changes.size()); cookie_changes.clear(); EXPECT_TRUE( this->FindAndDeleteCookie(cs, this->http_www_foo_.url().host(), "C")); EXPECT_TRUE( this->FindAndDeleteCookie(cs, this->http_www_foo_.url().host(), "G")); this->DeliverChangeNotifications(); // Check that the cookie changes are dispatched before calling GetCookies. // This is not an ASSERT because the following expectations produce useful // debugging information if they fail. EXPECT_EQ(2u, cookie_changes.size()); EXPECT_EQ("A=B; E=F", this->GetCookies(cs, this->http_www_foo_.url())); ASSERT_LE(1u, cookie_changes.size()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::EXPLICIT, cookie_changes[0].cause)); EXPECT_EQ("C", cookie_changes[0].cookie.Name()); EXPECT_EQ("D", cookie_changes[0].cookie.Value()); ASSERT_EQ(2u, cookie_changes.size()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[1].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::EXPLICIT, cookie_changes[1].cause)); EXPECT_EQ("G", cookie_changes[1].cookie.Name()); EXPECT_EQ("H", cookie_changes[1].cookie.Value()); } TYPED_TEST_P(CookieStoreChangeUrlTest, DeleteFiltering) { if (!TypeParam::supports_url_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForUrl( this->www_foo_foo_.url(), base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B; path=/")); EXPECT_TRUE(this->SetCookie(cs, this->http_bar_com_.url(), "C=D; path=/")); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "E=F; path=/bar")); EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "G=H; path=/foo/bar")); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "I=J; path=/foo")); EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "K=L; domain=foo.com")); this->DeliverChangeNotifications(); EXPECT_EQ(3u, cookie_changes.size()); cookie_changes.clear(); EXPECT_TRUE( this->FindAndDeleteCookie(cs, this->http_www_foo_.url().host(), "A")); EXPECT_TRUE( this->FindAndDeleteCookie(cs, this->http_bar_com_.url().host(), "C")); EXPECT_TRUE( this->FindAndDeleteCookie(cs, this->http_www_foo_.url().host(), "E")); EXPECT_TRUE( this->FindAndDeleteCookie(cs, this->http_www_foo_.url().host(), "G")); EXPECT_TRUE( this->FindAndDeleteCookie(cs, this->http_www_foo_.url().host(), "I")); EXPECT_TRUE(this->FindAndDeleteCookie(cs, ".foo.com", "K")); this->DeliverChangeNotifications(); ASSERT_LE(1u, cookie_changes.size()); EXPECT_EQ("A", cookie_changes[0].cookie.Name()); EXPECT_EQ("B", cookie_changes[0].cookie.Value()); EXPECT_EQ("/", cookie_changes[0].cookie.Path()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::EXPLICIT, cookie_changes[0].cause)); ASSERT_LE(2u, cookie_changes.size()); EXPECT_EQ("I", cookie_changes[1].cookie.Name()); EXPECT_EQ("J", cookie_changes[1].cookie.Value()); EXPECT_EQ("/foo", cookie_changes[1].cookie.Path()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[1].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::EXPLICIT, cookie_changes[1].cause)); ASSERT_LE(3u, cookie_changes.size()); EXPECT_EQ("K", cookie_changes[2].cookie.Name()); EXPECT_EQ("L", cookie_changes[2].cookie.Value()); EXPECT_EQ("/", cookie_changes[2].cookie.Path()); EXPECT_EQ(".foo.com", cookie_changes[2].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::EXPLICIT, cookie_changes[2].cause)); EXPECT_EQ(3u, cookie_changes.size()); } TYPED_TEST_P(CookieStoreChangeUrlTest, Overwrite) { if (!TypeParam::supports_url_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForUrl( this->http_www_foo_.url(), base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes.size()); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B")); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes.size()); cookie_changes.clear(); // Replacing an existing cookie is actually a two-phase delete + set // operation, so we get an extra notification. EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=C")); this->DeliverChangeNotifications(); ASSERT_LE(1u, cookie_changes.size()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].cookie.Domain()); EXPECT_TRUE(this->MatchesCause(CookieChangeCause::OVERWRITE, cookie_changes[0].cause)); EXPECT_EQ("A", cookie_changes[0].cookie.Name()); EXPECT_EQ("B", cookie_changes[0].cookie.Value()); ASSERT_LE(2u, cookie_changes.size()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[1].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::INSERTED, cookie_changes[1].cause)); EXPECT_EQ("A", cookie_changes[1].cookie.Name()); EXPECT_EQ("C", cookie_changes[1].cookie.Value()); EXPECT_EQ(2u, cookie_changes.size()); } TYPED_TEST_P(CookieStoreChangeUrlTest, OverwriteFiltering) { if (!TypeParam::supports_url_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForUrl( this->www_foo_foo_.url(), base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes.size()); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B; path=/")); EXPECT_TRUE(this->SetCookie(cs, this->http_bar_com_.url(), "C=D; path=/")); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "E=F; path=/bar")); EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "G=H; path=/foo/bar")); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "I=J; path=/foo")); EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "K=L; domain=foo.com")); this->DeliverChangeNotifications(); EXPECT_EQ(3u, cookie_changes.size()); cookie_changes.clear(); // Replacing an existing cookie is actually a two-phase delete + set // operation, so we get two notifications per overwrite. EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=b; path=/")); EXPECT_TRUE(this->SetCookie(cs, this->http_bar_com_.url(), "C=d; path=/")); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "E=f; path=/bar")); EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "G=h; path=/foo/bar")); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "I=j; path=/foo")); EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "K=l; domain=foo.com")); this->DeliverChangeNotifications(); ASSERT_LE(1u, cookie_changes.size()); EXPECT_EQ("A", cookie_changes[0].cookie.Name()); EXPECT_EQ("B", cookie_changes[0].cookie.Value()); EXPECT_EQ("/", cookie_changes[0].cookie.Path()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].cookie.Domain()); EXPECT_TRUE(this->MatchesCause(CookieChangeCause::OVERWRITE, cookie_changes[0].cause)); ASSERT_LE(2u, cookie_changes.size()); EXPECT_EQ("A", cookie_changes[1].cookie.Name()); EXPECT_EQ("b", cookie_changes[1].cookie.Value()); EXPECT_EQ("/", cookie_changes[1].cookie.Path()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[1].cookie.Domain()); EXPECT_EQ(CookieChangeCause::INSERTED, cookie_changes[1].cause); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::INSERTED, cookie_changes[1].cause)); ASSERT_LE(3u, cookie_changes.size()); EXPECT_EQ("I", cookie_changes[2].cookie.Name()); EXPECT_EQ("J", cookie_changes[2].cookie.Value()); EXPECT_EQ("/foo", cookie_changes[2].cookie.Path()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[2].cookie.Domain()); EXPECT_TRUE(this->MatchesCause(CookieChangeCause::OVERWRITE, cookie_changes[2].cause)); ASSERT_LE(4u, cookie_changes.size()); EXPECT_EQ("I", cookie_changes[3].cookie.Name()); EXPECT_EQ("j", cookie_changes[3].cookie.Value()); EXPECT_EQ("/foo", cookie_changes[3].cookie.Path()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[3].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::INSERTED, cookie_changes[3].cause)); ASSERT_LE(5u, cookie_changes.size()); EXPECT_EQ("K", cookie_changes[4].cookie.Name()); EXPECT_EQ("L", cookie_changes[4].cookie.Value()); EXPECT_EQ("/", cookie_changes[4].cookie.Path()); EXPECT_EQ(".foo.com", cookie_changes[4].cookie.Domain()); EXPECT_TRUE(this->MatchesCause(CookieChangeCause::OVERWRITE, cookie_changes[4].cause)); ASSERT_LE(6u, cookie_changes.size()); EXPECT_EQ("K", cookie_changes[5].cookie.Name()); EXPECT_EQ("l", cookie_changes[5].cookie.Value()); EXPECT_EQ("/", cookie_changes[5].cookie.Path()); EXPECT_EQ(".foo.com", cookie_changes[5].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::INSERTED, cookie_changes[5].cause)); EXPECT_EQ(6u, cookie_changes.size()); } TYPED_TEST_P(CookieStoreChangeUrlTest, OverwriteWithHttpOnly) { if (!TypeParam::supports_url_cookie_tracking) return; // Insert a cookie "A" for path "/foo". CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForUrl( this->www_foo_foo_.url(), base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes.size()); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B; path=/foo")); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes.size()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::INSERTED, cookie_changes[0].cause)); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].cookie.Domain()); EXPECT_EQ("A", cookie_changes[0].cookie.Name()); EXPECT_EQ("B", cookie_changes[0].cookie.Value()); EXPECT_FALSE(cookie_changes[0].cookie.IsHttpOnly()); cookie_changes.clear(); // Insert a cookie "A" for path "/foo", that is httponly. This should // overwrite the non-http-only version. CookieOptions allow_httponly; allow_httponly.set_include_httponly(); allow_httponly.set_same_site_cookie_context( net::CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT); EXPECT_TRUE(this->CreateAndSetCookie(cs, this->http_www_foo_.url(), "A=C; path=/foo; httponly", allow_httponly)); this->DeliverChangeNotifications(); ASSERT_LE(1u, cookie_changes.size()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].cookie.Domain()); EXPECT_TRUE(this->MatchesCause(CookieChangeCause::OVERWRITE, cookie_changes[0].cause)); EXPECT_EQ("A", cookie_changes[0].cookie.Name()); EXPECT_EQ("B", cookie_changes[0].cookie.Value()); EXPECT_FALSE(cookie_changes[0].cookie.IsHttpOnly()); ASSERT_LE(2u, cookie_changes.size()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[1].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::INSERTED, cookie_changes[1].cause)); EXPECT_EQ("A", cookie_changes[1].cookie.Name()); EXPECT_EQ("C", cookie_changes[1].cookie.Value()); EXPECT_TRUE(cookie_changes[1].cookie.IsHttpOnly()); EXPECT_EQ(2u, cookie_changes.size()); } TYPED_TEST_P(CookieStoreChangeUrlTest, Deregister) { if (!TypeParam::supports_url_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForUrl( this->http_www_foo_.url(), base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes.size()); // Insert a cookie and make sure it is seen. EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B")); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes.size()); EXPECT_EQ("A", cookie_changes[0].cookie.Name()); EXPECT_EQ("B", cookie_changes[0].cookie.Value()); cookie_changes.clear(); // De-register the subscription. subscription.reset(); // Insert a second cookie and make sure it's not visible. EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "C=D")); this->DeliverChangeNotifications(); EXPECT_EQ(0u, cookie_changes.size()); } TYPED_TEST_P(CookieStoreChangeUrlTest, DeregisterMultiple) { if (!TypeParam::supports_url_cookie_tracking || !TypeParam::supports_multiple_tracking_callbacks) return; CookieStore* cs = this->GetCookieStore(); // Register two subscriptions. std::vector cookie_changes_1, cookie_changes_2; std::unique_ptr subscription1 = cs->GetChangeDispatcher().AddCallbackForUrl( this->http_www_foo_.url(), base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_1))); std::unique_ptr subscription2 = cs->GetChangeDispatcher().AddCallbackForUrl( this->http_www_foo_.url(), base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_2))); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes_1.size()); ASSERT_EQ(0u, cookie_changes_2.size()); // Insert a cookie and make sure it's seen. EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B")); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes_1.size()); EXPECT_EQ("A", cookie_changes_1[0].cookie.Name()); EXPECT_EQ("B", cookie_changes_1[0].cookie.Value()); cookie_changes_1.clear(); ASSERT_EQ(1u, cookie_changes_2.size()); EXPECT_EQ("A", cookie_changes_2[0].cookie.Name()); EXPECT_EQ("B", cookie_changes_2[0].cookie.Value()); cookie_changes_2.clear(); // De-register the second registration. subscription2.reset(); // Insert a second cookie and make sure that it's only visible in one // change array. EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "C=D")); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes_1.size()); EXPECT_EQ("C", cookie_changes_1[0].cookie.Name()); EXPECT_EQ("D", cookie_changes_1[0].cookie.Value()); EXPECT_EQ(0u, cookie_changes_2.size()); } // Confirm that a listener does not receive notifications for changes that // happened right before the subscription was established. TYPED_TEST_P(CookieStoreChangeUrlTest, DispatchRace) { if (!TypeParam::supports_url_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); // This cookie insertion should not be seen. EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B")); // DeliverChangeNotifications() must NOT be called before the subscription is // established. std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForUrl( this->http_www_foo_.url(), base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "C=D")); this->DeliverChangeNotifications(); EXPECT_LE(1u, cookie_changes.size()); EXPECT_EQ("C", cookie_changes[0].cookie.Name()); EXPECT_EQ("D", cookie_changes[0].cookie.Value()); ASSERT_EQ(1u, cookie_changes.size()); } // Confirm that deregistering a subscription blocks the notification if the // deregistration happened after the change but before the notification was // received. TYPED_TEST_P(CookieStoreChangeUrlTest, DeregisterRace) { if (!TypeParam::supports_url_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForUrl( this->http_www_foo_.url(), base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes.size()); // Insert a cookie and make sure it's seen. EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B")); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes.size()); EXPECT_EQ("A", cookie_changes[0].cookie.Name()); EXPECT_EQ("B", cookie_changes[0].cookie.Value()); cookie_changes.clear(); // Insert a cookie, confirm it is not seen, deregister the subscription, run // until idle, and confirm the cookie is still not seen. EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "C=D")); // Note that by the API contract it's perfectly valid to have received the // notification immediately, i.e. synchronously with the cookie change. In // that case, there's nothing to test. if (1u == cookie_changes.size()) return; // A task was posted by the SetCookie() above, but has not yet arrived. If it // arrived before the subscription is destroyed, callback execution would be // valid. Destroy the subscription so as to lose the race and make sure the // task posted arrives after the subscription was destroyed. subscription.reset(); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes.size()); } TYPED_TEST_P(CookieStoreChangeUrlTest, DeregisterRaceMultiple) { if (!TypeParam::supports_url_cookie_tracking || !TypeParam::supports_multiple_tracking_callbacks) return; CookieStore* cs = this->GetCookieStore(); // Register two subscriptions. std::vector cookie_changes_1, cookie_changes_2; std::unique_ptr subscription1 = cs->GetChangeDispatcher().AddCallbackForUrl( this->http_www_foo_.url(), base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_1))); std::unique_ptr subscription2 = cs->GetChangeDispatcher().AddCallbackForUrl( this->http_www_foo_.url(), base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_2))); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes_1.size()); ASSERT_EQ(0u, cookie_changes_2.size()); // Insert a cookie and make sure it's seen. EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B")); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes_1.size()); EXPECT_EQ("A", cookie_changes_1[0].cookie.Name()); EXPECT_EQ("B", cookie_changes_1[0].cookie.Value()); cookie_changes_1.clear(); ASSERT_EQ(1u, cookie_changes_2.size()); EXPECT_EQ("A", cookie_changes_2[0].cookie.Name()); EXPECT_EQ("B", cookie_changes_2[0].cookie.Value()); cookie_changes_2.clear(); // Insert a cookie, confirm it is not seen, deregister a subscription, run // until idle, and confirm the cookie is still not seen. EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "C=D")); // Note that by the API contract it's perfectly valid to have received the // notification immediately, i.e. synchronously with the cookie change. In // that case, there's nothing to test. if (1u == cookie_changes_2.size()) return; // A task was posted by the SetCookie() above, but has not yet arrived. If it // arrived before the subscription is destroyed, callback execution would be // valid. Destroy one of the subscriptions so as to lose the race and make // sure the task posted arrives after the subscription was destroyed. subscription2.reset(); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes_1.size()); EXPECT_EQ("C", cookie_changes_1[0].cookie.Name()); EXPECT_EQ("D", cookie_changes_1[0].cookie.Value()); // No late notification was received. ASSERT_EQ(0u, cookie_changes_2.size()); } TYPED_TEST_P(CookieStoreChangeUrlTest, DifferentSubscriptionsDisjoint) { if (!TypeParam::supports_url_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes_1, cookie_changes_2; std::unique_ptr subscription1 = cs->GetChangeDispatcher().AddCallbackForUrl( this->http_www_foo_.url(), base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_1))); std::unique_ptr subscription2 = cs->GetChangeDispatcher().AddCallbackForUrl( this->http_bar_com_.url(), base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_2))); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes_1.size()); ASSERT_EQ(0u, cookie_changes_2.size()); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B")); this->DeliverChangeNotifications(); EXPECT_EQ(1u, cookie_changes_1.size()); EXPECT_EQ(0u, cookie_changes_2.size()); EXPECT_TRUE(this->SetCookie(cs, this->http_bar_com_.url(), "C=D")); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes_1.size()); EXPECT_EQ("A", cookie_changes_1[0].cookie.Name()); EXPECT_EQ("B", cookie_changes_1[0].cookie.Value()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes_1[0].cookie.Domain()); ASSERT_EQ(1u, cookie_changes_2.size()); EXPECT_EQ("C", cookie_changes_2[0].cookie.Name()); EXPECT_EQ("D", cookie_changes_2[0].cookie.Value()); EXPECT_EQ(this->http_bar_com_.url().host(), cookie_changes_2[0].cookie.Domain()); } TYPED_TEST_P(CookieStoreChangeUrlTest, DifferentSubscriptionsDomains) { if (!TypeParam::supports_url_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes_1, cookie_changes_2; std::unique_ptr subscription1 = cs->GetChangeDispatcher().AddCallbackForUrl( this->http_www_foo_.url(), base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_1))); std::unique_ptr subscription2 = cs->GetChangeDispatcher().AddCallbackForUrl( this->http_bar_com_.url(), base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_2))); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes_1.size()); ASSERT_EQ(0u, cookie_changes_2.size()); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B")); this->DeliverChangeNotifications(); EXPECT_EQ(1u, cookie_changes_1.size()); EXPECT_EQ(0u, cookie_changes_2.size()); EXPECT_TRUE(this->SetCookie(cs, this->http_bar_com_.url(), "C=D")); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes_1.size()); EXPECT_EQ("A", cookie_changes_1[0].cookie.Name()); EXPECT_EQ("B", cookie_changes_1[0].cookie.Value()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes_1[0].cookie.Domain()); ASSERT_EQ(1u, cookie_changes_2.size()); EXPECT_EQ("C", cookie_changes_2[0].cookie.Name()); EXPECT_EQ("D", cookie_changes_2[0].cookie.Value()); EXPECT_EQ(this->http_bar_com_.url().host(), cookie_changes_2[0].cookie.Domain()); } TYPED_TEST_P(CookieStoreChangeUrlTest, DifferentSubscriptionsPaths) { if (!TypeParam::supports_url_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes_1, cookie_changes_2; std::unique_ptr subscription1 = cs->GetChangeDispatcher().AddCallbackForUrl( this->http_www_foo_.url(), base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_1))); std::unique_ptr subscription2 = cs->GetChangeDispatcher().AddCallbackForUrl( this->www_foo_foo_.url(), base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_2))); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes_1.size()); ASSERT_EQ(0u, cookie_changes_2.size()); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B")); this->DeliverChangeNotifications(); EXPECT_EQ(1u, cookie_changes_1.size()); EXPECT_EQ(1u, cookie_changes_2.size()); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "C=D; path=/foo")); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes_1.size()); EXPECT_EQ("A", cookie_changes_1[0].cookie.Name()); EXPECT_EQ("B", cookie_changes_1[0].cookie.Value()); EXPECT_EQ("/", cookie_changes_1[0].cookie.Path()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes_1[0].cookie.Domain()); ASSERT_LE(1u, cookie_changes_2.size()); EXPECT_EQ("A", cookie_changes_2[0].cookie.Name()); EXPECT_EQ("B", cookie_changes_2[0].cookie.Value()); EXPECT_EQ("/", cookie_changes_2[0].cookie.Path()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes_2[0].cookie.Domain()); ASSERT_LE(2u, cookie_changes_2.size()); EXPECT_EQ("C", cookie_changes_2[1].cookie.Name()); EXPECT_EQ("D", cookie_changes_2[1].cookie.Value()); EXPECT_EQ("/foo", cookie_changes_2[1].cookie.Path()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes_2[1].cookie.Domain()); EXPECT_EQ(2u, cookie_changes_2.size()); } TYPED_TEST_P(CookieStoreChangeUrlTest, DifferentSubscriptionsFiltering) { if (!TypeParam::supports_url_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes_1, cookie_changes_2; std::vector cookie_changes_3; std::unique_ptr subscription1 = cs->GetChangeDispatcher().AddCallbackForUrl( this->http_www_foo_.url(), base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_1))); std::unique_ptr subscription2 = cs->GetChangeDispatcher().AddCallbackForUrl( this->http_bar_com_.url(), base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_2))); std::unique_ptr subscription3 = cs->GetChangeDispatcher().AddCallbackForUrl( this->www_foo_foo_.url(), base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_3))); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes_1.size()); ASSERT_EQ(0u, cookie_changes_2.size()); EXPECT_EQ(0u, cookie_changes_3.size()); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B")); this->DeliverChangeNotifications(); EXPECT_EQ(1u, cookie_changes_1.size()); EXPECT_EQ(0u, cookie_changes_2.size()); EXPECT_EQ(1u, cookie_changes_3.size()); EXPECT_TRUE(this->SetCookie(cs, this->http_bar_com_.url(), "C=D")); this->DeliverChangeNotifications(); EXPECT_EQ(1u, cookie_changes_1.size()); EXPECT_EQ(1u, cookie_changes_2.size()); EXPECT_EQ(1u, cookie_changes_3.size()); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "E=F; path=/foo")); this->DeliverChangeNotifications(); ASSERT_LE(1u, cookie_changes_1.size()); EXPECT_EQ("A", cookie_changes_1[0].cookie.Name()); EXPECT_EQ("B", cookie_changes_1[0].cookie.Value()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes_1[0].cookie.Domain()); EXPECT_EQ(1u, cookie_changes_1.size()); ASSERT_LE(1u, cookie_changes_2.size()); EXPECT_EQ("C", cookie_changes_2[0].cookie.Name()); EXPECT_EQ("D", cookie_changes_2[0].cookie.Value()); EXPECT_EQ(this->http_bar_com_.url().host(), cookie_changes_2[0].cookie.Domain()); EXPECT_EQ(1u, cookie_changes_2.size()); ASSERT_LE(1u, cookie_changes_3.size()); EXPECT_EQ("A", cookie_changes_3[0].cookie.Name()); EXPECT_EQ("B", cookie_changes_3[0].cookie.Value()); EXPECT_EQ("/", cookie_changes_3[0].cookie.Path()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes_3[0].cookie.Domain()); ASSERT_LE(2u, cookie_changes_3.size()); EXPECT_EQ("E", cookie_changes_3[1].cookie.Name()); EXPECT_EQ("F", cookie_changes_3[1].cookie.Value()); EXPECT_EQ("/foo", cookie_changes_3[1].cookie.Path()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes_3[1].cookie.Domain()); EXPECT_EQ(2u, cookie_changes_3.size()); } TYPED_TEST_P(CookieStoreChangeUrlTest, MultipleSubscriptions) { if (!TypeParam::supports_url_cookie_tracking || !TypeParam::supports_multiple_tracking_callbacks) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes_1, cookie_changes_2; std::unique_ptr subscription1 = cs->GetChangeDispatcher().AddCallbackForUrl( this->http_www_foo_.url(), base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_1))); std::unique_ptr subscription2 = cs->GetChangeDispatcher().AddCallbackForUrl( this->http_www_foo_.url(), base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_2))); this->DeliverChangeNotifications(); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "A=B")); this->DeliverChangeNotifications(); ASSERT_EQ(1U, cookie_changes_1.size()); EXPECT_EQ("A", cookie_changes_1[0].cookie.Name()); EXPECT_EQ("B", cookie_changes_1[0].cookie.Value()); ASSERT_EQ(1U, cookie_changes_2.size()); EXPECT_EQ("A", cookie_changes_2[0].cookie.Name()); EXPECT_EQ("B", cookie_changes_2[0].cookie.Value()); } TYPED_TEST_P(CookieStoreChangeUrlTest, ChangeIncludesCookieAccessSemantics) { if (!TypeParam::supports_url_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); // if !supports_cookie_access_semantics, the delegate will be stored but will // not be used. auto access_delegate = std::make_unique(); access_delegate->SetExpectationForCookieDomain("domain1.test", CookieAccessSemantics::LEGACY); cs->SetCookieAccessDelegate(std::move(access_delegate)); std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForUrl( GURL("http://domain1.test"), base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); this->CreateAndSetCookie(cs, GURL("http://domain1.test"), "cookie=1", CookieOptions::MakeAllInclusive()); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes.size()); EXPECT_EQ("domain1.test", cookie_changes[0].cookie.Domain()); EXPECT_TRUE(this->IsExpectedAccessSemantics( CookieAccessSemantics::LEGACY, cookie_changes[0].access_semantics)); } TYPED_TEST_P(CookieStoreChangeNamedTest, NoCookie) { if (!TypeParam::supports_named_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForCookie( this->http_www_foo_.url(), "abc", base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); this->DeliverChangeNotifications(); EXPECT_EQ(0u, cookie_changes.size()); } TYPED_TEST_P(CookieStoreChangeNamedTest, InitialCookie) { if (!TypeParam::supports_named_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes; this->SetCookie(cs, this->http_www_foo_.url(), "abc=def"); this->DeliverChangeNotifications(); std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForCookie( this->http_www_foo_.url(), "abc", base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); this->DeliverChangeNotifications(); EXPECT_EQ(0u, cookie_changes.size()); } TYPED_TEST_P(CookieStoreChangeNamedTest, InsertOne) { if (!TypeParam::supports_named_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForCookie( this->http_www_foo_.url(), "abc", base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes.size()); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "abc=def")); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes.size()); EXPECT_EQ("abc", cookie_changes[0].cookie.Name()); EXPECT_EQ("def", cookie_changes[0].cookie.Value()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::INSERTED, cookie_changes[0].cause)); } TYPED_TEST_P(CookieStoreChangeNamedTest, InsertTwo) { if (!TypeParam::supports_named_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForCookie( this->www_foo_foo_.url(), "abc", base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes.size()); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "abc=def")); EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "abc=hij; path=/foo")); this->DeliverChangeNotifications(); ASSERT_LE(1u, cookie_changes.size()); EXPECT_EQ("abc", cookie_changes[0].cookie.Name()); EXPECT_EQ("def", cookie_changes[0].cookie.Value()); EXPECT_EQ("/", cookie_changes[0].cookie.Path()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::INSERTED, cookie_changes[0].cause)); ASSERT_LE(2u, cookie_changes.size()); EXPECT_EQ("abc", cookie_changes[1].cookie.Name()); EXPECT_EQ("hij", cookie_changes[1].cookie.Value()); EXPECT_EQ("/foo", cookie_changes[1].cookie.Path()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[1].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::INSERTED, cookie_changes[1].cause)); EXPECT_EQ(2u, cookie_changes.size()); } TYPED_TEST_P(CookieStoreChangeNamedTest, InsertFiltering) { if (!TypeParam::supports_named_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForCookie( this->www_foo_foo_.url(), "abc", base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes.size()); EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "abc=def; path=/")); EXPECT_TRUE( this->SetCookie(cs, this->http_bar_com_.url(), "abc=ghi; path=/")); EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "abc=jkl; path=/bar")); EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "abc=mno; path=/foo/bar")); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "xyz=zyx")); EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "abc=pqr; path=/foo")); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "abc=stu; domain=foo.com")); this->DeliverChangeNotifications(); ASSERT_LE(1u, cookie_changes.size()); EXPECT_EQ("abc", cookie_changes[0].cookie.Name()); EXPECT_EQ("def", cookie_changes[0].cookie.Value()); EXPECT_EQ("/", cookie_changes[0].cookie.Path()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::INSERTED, cookie_changes[0].cause)); ASSERT_LE(2u, cookie_changes.size()); EXPECT_EQ("abc", cookie_changes[1].cookie.Name()); EXPECT_EQ("pqr", cookie_changes[1].cookie.Value()); EXPECT_EQ("/foo", cookie_changes[1].cookie.Path()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[1].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::INSERTED, cookie_changes[1].cause)); ASSERT_LE(3u, cookie_changes.size()); EXPECT_EQ("abc", cookie_changes[2].cookie.Name()); EXPECT_EQ("stu", cookie_changes[2].cookie.Value()); EXPECT_EQ("/", cookie_changes[2].cookie.Path()); EXPECT_EQ(".foo.com", cookie_changes[2].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::INSERTED, cookie_changes[2].cause)); EXPECT_EQ(3u, cookie_changes.size()); } TYPED_TEST_P(CookieStoreChangeNamedTest, DeleteOne) { if (!TypeParam::supports_named_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForCookie( this->http_www_foo_.url(), "abc", base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "abc=def")); this->DeliverChangeNotifications(); EXPECT_EQ(1u, cookie_changes.size()); cookie_changes.clear(); EXPECT_TRUE( this->FindAndDeleteCookie(cs, this->http_www_foo_.url().host(), "abc")); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes.size()); EXPECT_EQ("abc", cookie_changes[0].cookie.Name()); EXPECT_EQ("def", cookie_changes[0].cookie.Value()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::EXPLICIT, cookie_changes[0].cause)); } TYPED_TEST_P(CookieStoreChangeNamedTest, DeleteTwo) { if (!TypeParam::supports_named_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForCookie( this->www_foo_foo_.url(), "abc", base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "abc=def")); EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "abc=hij; path=/foo")); this->DeliverChangeNotifications(); EXPECT_EQ(2u, cookie_changes.size()); cookie_changes.clear(); EXPECT_TRUE(this->FindAndDeleteCookie(cs, this->http_www_foo_.url().host(), "abc", "/")); EXPECT_TRUE(this->FindAndDeleteCookie(cs, this->http_www_foo_.url().host(), "abc", "/foo")); this->DeliverChangeNotifications(); ASSERT_LE(1u, cookie_changes.size()); EXPECT_EQ("abc", cookie_changes[0].cookie.Name()); EXPECT_EQ("def", cookie_changes[0].cookie.Value()); EXPECT_EQ("/", cookie_changes[0].cookie.Path()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::EXPLICIT, cookie_changes[0].cause)); ASSERT_EQ(2u, cookie_changes.size()); EXPECT_EQ("abc", cookie_changes[1].cookie.Name()); EXPECT_EQ("hij", cookie_changes[1].cookie.Value()); EXPECT_EQ("/foo", cookie_changes[1].cookie.Path()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[1].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::EXPLICIT, cookie_changes[1].cause)); } TYPED_TEST_P(CookieStoreChangeNamedTest, DeleteFiltering) { if (!TypeParam::supports_named_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForCookie( this->www_foo_foo_.url(), "abc", base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "xyz=zyx; path=/")); EXPECT_TRUE( this->SetCookie(cs, this->http_bar_com_.url(), "abc=def; path=/")); EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "abc=hij; path=/foo/bar")); EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "abc=mno; path=/foo")); EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "abc=pqr; path=/")); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "abc=stu; domain=foo.com")); this->DeliverChangeNotifications(); EXPECT_EQ(3u, cookie_changes.size()); cookie_changes.clear(); EXPECT_TRUE( this->FindAndDeleteCookie(cs, this->http_www_foo_.url().host(), "xyz")); EXPECT_TRUE( this->FindAndDeleteCookie(cs, this->http_bar_com_.url().host(), "abc")); EXPECT_TRUE(this->FindAndDeleteCookie(cs, this->http_www_foo_.url().host(), "abc", "/foo/bar")); EXPECT_TRUE(this->FindAndDeleteCookie(cs, this->http_www_foo_.url().host(), "abc", "/foo")); EXPECT_TRUE(this->FindAndDeleteCookie(cs, this->http_www_foo_.url().host(), "abc", "/")); EXPECT_TRUE(this->FindAndDeleteCookie(cs, ".foo.com", "abc", "/")); this->DeliverChangeNotifications(); ASSERT_LE(1u, cookie_changes.size()); EXPECT_EQ("abc", cookie_changes[0].cookie.Name()); EXPECT_EQ("mno", cookie_changes[0].cookie.Value()); EXPECT_EQ("/foo", cookie_changes[0].cookie.Path()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::EXPLICIT, cookie_changes[0].cause)); ASSERT_LE(2u, cookie_changes.size()); EXPECT_EQ("abc", cookie_changes[1].cookie.Name()); EXPECT_EQ("pqr", cookie_changes[1].cookie.Value()); EXPECT_EQ("/", cookie_changes[1].cookie.Path()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[1].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::EXPLICIT, cookie_changes[1].cause)); ASSERT_LE(3u, cookie_changes.size()); EXPECT_EQ("abc", cookie_changes[2].cookie.Name()); EXPECT_EQ("stu", cookie_changes[2].cookie.Value()); EXPECT_EQ("/", cookie_changes[2].cookie.Path()); EXPECT_EQ(".foo.com", cookie_changes[2].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::EXPLICIT, cookie_changes[2].cause)); EXPECT_EQ(3u, cookie_changes.size()); } TYPED_TEST_P(CookieStoreChangeNamedTest, Overwrite) { if (!TypeParam::supports_named_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForCookie( this->http_www_foo_.url(), "abc", base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes.size()); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "abc=def")); this->DeliverChangeNotifications(); EXPECT_EQ(1u, cookie_changes.size()); cookie_changes.clear(); // Replacing an existing cookie is actually a two-phase delete + set // operation, so we get an extra notification. EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "abc=ghi")); this->DeliverChangeNotifications(); EXPECT_LE(1u, cookie_changes.size()); EXPECT_EQ("abc", cookie_changes[0].cookie.Name()); EXPECT_EQ("def", cookie_changes[0].cookie.Value()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].cookie.Domain()); EXPECT_TRUE(this->MatchesCause(CookieChangeCause::OVERWRITE, cookie_changes[0].cause)); EXPECT_LE(2u, cookie_changes.size()); EXPECT_EQ("abc", cookie_changes[1].cookie.Name()); EXPECT_EQ("ghi", cookie_changes[1].cookie.Value()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[1].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::INSERTED, cookie_changes[1].cause)); EXPECT_EQ(2u, cookie_changes.size()); } TYPED_TEST_P(CookieStoreChangeNamedTest, OverwriteFiltering) { if (!TypeParam::supports_named_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForCookie( this->www_foo_foo_.url(), "abc", base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes.size()); EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "xyz=zyx1; path=/")); EXPECT_TRUE( this->SetCookie(cs, this->http_bar_com_.url(), "abc=def1; path=/")); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "abc=hij1; path=/foo/bar")); EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "abc=mno1; path=/foo")); EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "abc=pqr1; path=/")); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "abc=stu1; domain=foo.com")); this->DeliverChangeNotifications(); EXPECT_EQ(3u, cookie_changes.size()); cookie_changes.clear(); // Replacing an existing cookie is actually a two-phase delete + set // operation, so we get two notifications per overwrite. EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "xyz=zyx2; path=/")); EXPECT_TRUE( this->SetCookie(cs, this->http_bar_com_.url(), "abc=def2; path=/")); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "abc=hij2; path=/foo/bar")); EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "abc=mno2; path=/foo")); EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "abc=pqr2; path=/")); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "abc=stu2; domain=foo.com")); this->DeliverChangeNotifications(); ASSERT_LE(1u, cookie_changes.size()); EXPECT_EQ("abc", cookie_changes[0].cookie.Name()); EXPECT_EQ("mno1", cookie_changes[0].cookie.Value()); EXPECT_EQ("/foo", cookie_changes[0].cookie.Path()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].cookie.Domain()); EXPECT_TRUE(this->MatchesCause(CookieChangeCause::OVERWRITE, cookie_changes[0].cause)); ASSERT_LE(2u, cookie_changes.size()); EXPECT_EQ("abc", cookie_changes[1].cookie.Name()); EXPECT_EQ("mno2", cookie_changes[1].cookie.Value()); EXPECT_EQ("/foo", cookie_changes[1].cookie.Path()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[1].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::INSERTED, cookie_changes[1].cause)); ASSERT_LE(3u, cookie_changes.size()); EXPECT_EQ("abc", cookie_changes[2].cookie.Name()); EXPECT_EQ("pqr1", cookie_changes[2].cookie.Value()); EXPECT_EQ("/", cookie_changes[2].cookie.Path()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[2].cookie.Domain()); EXPECT_TRUE(this->MatchesCause(CookieChangeCause::OVERWRITE, cookie_changes[2].cause)); ASSERT_LE(4u, cookie_changes.size()); EXPECT_EQ("abc", cookie_changes[3].cookie.Name()); EXPECT_EQ("pqr2", cookie_changes[3].cookie.Value()); EXPECT_EQ("/", cookie_changes[3].cookie.Path()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[3].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::INSERTED, cookie_changes[3].cause)); ASSERT_LE(5u, cookie_changes.size()); EXPECT_EQ("abc", cookie_changes[4].cookie.Name()); EXPECT_EQ("stu1", cookie_changes[4].cookie.Value()); EXPECT_EQ("/", cookie_changes[4].cookie.Path()); EXPECT_EQ(".foo.com", cookie_changes[4].cookie.Domain()); EXPECT_TRUE(this->MatchesCause(CookieChangeCause::OVERWRITE, cookie_changes[4].cause)); ASSERT_LE(6u, cookie_changes.size()); EXPECT_EQ("abc", cookie_changes[5].cookie.Name()); EXPECT_EQ("stu2", cookie_changes[5].cookie.Value()); EXPECT_EQ("/", cookie_changes[5].cookie.Path()); EXPECT_EQ(".foo.com", cookie_changes[5].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::INSERTED, cookie_changes[5].cause)); EXPECT_EQ(6u, cookie_changes.size()); } TYPED_TEST_P(CookieStoreChangeNamedTest, OverwriteWithHttpOnly) { if (!TypeParam::supports_named_cookie_tracking) return; // Insert a cookie "abc" for path "/foo". CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForCookie( this->www_foo_foo_.url(), "abc", base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes.size()); EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "abc=def; path=/foo")); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes.size()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::INSERTED, cookie_changes[0].cause)); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].cookie.Domain()); EXPECT_EQ("abc", cookie_changes[0].cookie.Name()); EXPECT_EQ("def", cookie_changes[0].cookie.Value()); EXPECT_FALSE(cookie_changes[0].cookie.IsHttpOnly()); cookie_changes.clear(); // Insert a cookie "a" for path "/foo", that is httponly. This should // overwrite the non-http-only version. CookieOptions allow_httponly; allow_httponly.set_include_httponly(); allow_httponly.set_same_site_cookie_context( net::CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT); EXPECT_TRUE(this->CreateAndSetCookie(cs, this->http_www_foo_.url(), "abc=hij; path=/foo; httponly", allow_httponly)); this->DeliverChangeNotifications(); ASSERT_LE(1u, cookie_changes.size()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].cookie.Domain()); EXPECT_TRUE(this->MatchesCause(CookieChangeCause::OVERWRITE, cookie_changes[0].cause)); EXPECT_EQ("abc", cookie_changes[0].cookie.Name()); EXPECT_EQ("def", cookie_changes[0].cookie.Value()); EXPECT_FALSE(cookie_changes[0].cookie.IsHttpOnly()); ASSERT_LE(2u, cookie_changes.size()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[1].cookie.Domain()); EXPECT_TRUE( this->MatchesCause(CookieChangeCause::INSERTED, cookie_changes[1].cause)); EXPECT_EQ("abc", cookie_changes[1].cookie.Name()); EXPECT_EQ("hij", cookie_changes[1].cookie.Value()); EXPECT_TRUE(cookie_changes[1].cookie.IsHttpOnly()); EXPECT_EQ(2u, cookie_changes.size()); } TYPED_TEST_P(CookieStoreChangeNamedTest, Deregister) { if (!TypeParam::supports_named_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForCookie( this->www_foo_foo_.url(), "abc", base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes.size()); // Insert a cookie and make sure it is seen. EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "abc=def; path=/foo")); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes.size()); EXPECT_EQ("abc", cookie_changes[0].cookie.Name()); EXPECT_EQ("def", cookie_changes[0].cookie.Value()); EXPECT_EQ("/foo", cookie_changes[0].cookie.Path()); cookie_changes.clear(); // De-register the subscription. subscription.reset(); // Insert a second cookie and make sure it's not visible. EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "abc=hij; path=/")); this->DeliverChangeNotifications(); EXPECT_EQ(0u, cookie_changes.size()); } TYPED_TEST_P(CookieStoreChangeNamedTest, DeregisterMultiple) { if (!TypeParam::supports_named_cookie_tracking || !TypeParam::supports_multiple_tracking_callbacks) return; CookieStore* cs = this->GetCookieStore(); // Register two subscriptions. std::vector cookie_changes_1, cookie_changes_2; std::unique_ptr subscription1 = cs->GetChangeDispatcher().AddCallbackForCookie( this->www_foo_foo_.url(), "abc", base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_1))); std::unique_ptr subscription2 = cs->GetChangeDispatcher().AddCallbackForCookie( this->www_foo_foo_.url(), "abc", base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_2))); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes_1.size()); ASSERT_EQ(0u, cookie_changes_2.size()); // Insert a cookie and make sure it's seen. EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "abc=def; path=/foo")); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes_1.size()); EXPECT_EQ("abc", cookie_changes_1[0].cookie.Name()); EXPECT_EQ("def", cookie_changes_1[0].cookie.Value()); EXPECT_EQ("/foo", cookie_changes_1[0].cookie.Path()); cookie_changes_1.clear(); ASSERT_EQ(1u, cookie_changes_2.size()); EXPECT_EQ("abc", cookie_changes_2[0].cookie.Name()); EXPECT_EQ("def", cookie_changes_2[0].cookie.Value()); EXPECT_EQ("/foo", cookie_changes_2[0].cookie.Path()); cookie_changes_2.clear(); // De-register the second registration. subscription2.reset(); // Insert a second cookie and make sure that it's only visible in one // change array. EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "abc=hij; path=/")); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes_1.size()); EXPECT_EQ("abc", cookie_changes_1[0].cookie.Name()); EXPECT_EQ("hij", cookie_changes_1[0].cookie.Value()); EXPECT_EQ("/", cookie_changes_1[0].cookie.Path()); EXPECT_EQ(0u, cookie_changes_2.size()); } // Confirm that a listener does not receive notifications for changes that // happened right before the subscription was established. TYPED_TEST_P(CookieStoreChangeNamedTest, DispatchRace) { if (!TypeParam::supports_named_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); // This cookie insertion should not be seen. EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "abc=def; path=/foo")); // DeliverChangeNotifications() must NOT be called before the subscription is // established. std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForCookie( this->www_foo_foo_.url(), "abc", base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "abc=hij; path=/")); this->DeliverChangeNotifications(); EXPECT_LE(1u, cookie_changes.size()); EXPECT_EQ("abc", cookie_changes[0].cookie.Name()); EXPECT_EQ("hij", cookie_changes[0].cookie.Value()); EXPECT_EQ("/", cookie_changes[0].cookie.Path()); ASSERT_EQ(1u, cookie_changes.size()); } // Confirm that deregistering a subscription blocks the notification if the // deregistration happened after the change but before the notification was // received. TYPED_TEST_P(CookieStoreChangeNamedTest, DeregisterRace) { if (!TypeParam::supports_named_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForCookie( this->www_foo_foo_.url(), "abc", base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes.size()); // Insert a cookie and make sure it's seen. EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "abc=def; path=/foo")); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes.size()); EXPECT_EQ("abc", cookie_changes[0].cookie.Name()); EXPECT_EQ("def", cookie_changes[0].cookie.Value()); EXPECT_EQ("/foo", cookie_changes[0].cookie.Path()); cookie_changes.clear(); // Insert a cookie, confirm it is not seen, deregister the subscription, run // until idle, and confirm the cookie is still not seen. EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "abc=hij; path=/")); // Note that by the API contract it's perfectly valid to have received the // notification immediately, i.e. synchronously with the cookie change. In // that case, there's nothing to test. if (1u == cookie_changes.size()) return; // A task was posted by the SetCookie() above, but has not yet arrived. If it // arrived before the subscription is destroyed, callback execution would be // valid. Destroy the subscription so as to lose the race and make sure the // task posted arrives after the subscription was destroyed. subscription.reset(); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes.size()); } TYPED_TEST_P(CookieStoreChangeNamedTest, DeregisterRaceMultiple) { if (!TypeParam::supports_named_cookie_tracking || !TypeParam::supports_multiple_tracking_callbacks) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes_1, cookie_changes_2; std::unique_ptr subscription1 = cs->GetChangeDispatcher().AddCallbackForCookie( this->www_foo_foo_.url(), "abc", base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_1))); std::unique_ptr subscription2 = cs->GetChangeDispatcher().AddCallbackForCookie( this->www_foo_foo_.url(), "abc", base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_2))); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes_1.size()); ASSERT_EQ(0u, cookie_changes_2.size()); // Insert a cookie and make sure it's seen. EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "abc=def; path=/foo")); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes_1.size()); EXPECT_EQ("abc", cookie_changes_1[0].cookie.Name()); EXPECT_EQ("def", cookie_changes_1[0].cookie.Value()); EXPECT_EQ("/foo", cookie_changes_1[0].cookie.Path()); cookie_changes_1.clear(); ASSERT_EQ(1u, cookie_changes_2.size()); EXPECT_EQ("abc", cookie_changes_2[0].cookie.Name()); EXPECT_EQ("def", cookie_changes_2[0].cookie.Value()); EXPECT_EQ("/foo", cookie_changes_2[0].cookie.Path()); cookie_changes_2.clear(); // Insert a cookie, confirm it is not seen, deregister a subscription, run // until idle, and confirm the cookie is still not seen. EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "abc=hij; path=/")); // Note that by the API contract it's perfectly valid to have received the // notification immediately, i.e. synchronously with the cookie change. In // that case, there's nothing to test. if (1u == cookie_changes_2.size()) return; // A task was posted by the SetCookie() above, but has not yet arrived. If it // arrived before the subscription is destroyed, callback execution would be // valid. Destroy one of the subscriptions so as to lose the race and make // sure the task posted arrives after the subscription was destroyed. subscription2.reset(); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes_1.size()); EXPECT_EQ("abc", cookie_changes_1[0].cookie.Name()); EXPECT_EQ("hij", cookie_changes_1[0].cookie.Value()); EXPECT_EQ("/", cookie_changes_1[0].cookie.Path()); // No late notification was received. ASSERT_EQ(0u, cookie_changes_2.size()); } TYPED_TEST_P(CookieStoreChangeNamedTest, DifferentSubscriptionsDisjoint) { if (!TypeParam::supports_named_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes_1, cookie_changes_2; std::unique_ptr subscription1 = cs->GetChangeDispatcher().AddCallbackForCookie( this->http_www_foo_.url(), "abc", base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_1))); std::unique_ptr subscription2 = cs->GetChangeDispatcher().AddCallbackForCookie( this->http_bar_com_.url(), "ghi", base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_2))); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes_1.size()); ASSERT_EQ(0u, cookie_changes_2.size()); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "abc=def")); this->DeliverChangeNotifications(); EXPECT_EQ(1u, cookie_changes_1.size()); EXPECT_EQ(0u, cookie_changes_2.size()); EXPECT_TRUE(this->SetCookie(cs, this->http_bar_com_.url(), "ghi=jkl")); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes_1.size()); EXPECT_EQ("abc", cookie_changes_1[0].cookie.Name()); EXPECT_EQ("def", cookie_changes_1[0].cookie.Value()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes_1[0].cookie.Domain()); ASSERT_EQ(1u, cookie_changes_2.size()); EXPECT_EQ("ghi", cookie_changes_2[0].cookie.Name()); EXPECT_EQ("jkl", cookie_changes_2[0].cookie.Value()); EXPECT_EQ(this->http_bar_com_.url().host(), cookie_changes_2[0].cookie.Domain()); } TYPED_TEST_P(CookieStoreChangeNamedTest, DifferentSubscriptionsDomains) { if (!TypeParam::supports_named_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes_1, cookie_changes_2; std::unique_ptr subscription1 = cs->GetChangeDispatcher().AddCallbackForCookie( this->http_www_foo_.url(), "abc", base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_1))); std::unique_ptr subscription2 = cs->GetChangeDispatcher().AddCallbackForCookie( this->http_bar_com_.url(), "abc", base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_2))); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes_1.size()); ASSERT_EQ(0u, cookie_changes_2.size()); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "abc=def")); this->DeliverChangeNotifications(); EXPECT_EQ(1u, cookie_changes_1.size()); EXPECT_EQ(0u, cookie_changes_2.size()); EXPECT_TRUE(this->SetCookie(cs, this->http_bar_com_.url(), "abc=ghi")); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes_1.size()); EXPECT_EQ("abc", cookie_changes_1[0].cookie.Name()); EXPECT_EQ("def", cookie_changes_1[0].cookie.Value()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes_1[0].cookie.Domain()); ASSERT_EQ(1u, cookie_changes_2.size()); EXPECT_EQ("abc", cookie_changes_2[0].cookie.Name()); EXPECT_EQ("ghi", cookie_changes_2[0].cookie.Value()); EXPECT_EQ(this->http_bar_com_.url().host(), cookie_changes_2[0].cookie.Domain()); } TYPED_TEST_P(CookieStoreChangeNamedTest, DifferentSubscriptionsNames) { if (!TypeParam::supports_named_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes_1, cookie_changes_2; std::unique_ptr subscription1 = cs->GetChangeDispatcher().AddCallbackForCookie( this->http_www_foo_.url(), "abc", base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_1))); std::unique_ptr subscription2 = cs->GetChangeDispatcher().AddCallbackForCookie( this->http_www_foo_.url(), "ghi", base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_2))); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes_1.size()); ASSERT_EQ(0u, cookie_changes_2.size()); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "abc=def")); this->DeliverChangeNotifications(); EXPECT_EQ(1u, cookie_changes_1.size()); EXPECT_EQ(0u, cookie_changes_2.size()); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "ghi=jkl")); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes_1.size()); EXPECT_EQ("abc", cookie_changes_1[0].cookie.Name()); EXPECT_EQ("def", cookie_changes_1[0].cookie.Value()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes_1[0].cookie.Domain()); ASSERT_EQ(1u, cookie_changes_2.size()); EXPECT_EQ("ghi", cookie_changes_2[0].cookie.Name()); EXPECT_EQ("jkl", cookie_changes_2[0].cookie.Value()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes_2[0].cookie.Domain()); } TYPED_TEST_P(CookieStoreChangeNamedTest, DifferentSubscriptionsPaths) { if (!TypeParam::supports_named_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes_1, cookie_changes_2; std::unique_ptr subscription1 = cs->GetChangeDispatcher().AddCallbackForCookie( this->http_www_foo_.url(), "abc", base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_1))); std::unique_ptr subscription2 = cs->GetChangeDispatcher().AddCallbackForCookie( this->www_foo_foo_.url(), "abc", base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_2))); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes_1.size()); ASSERT_EQ(0u, cookie_changes_2.size()); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "abc=def")); this->DeliverChangeNotifications(); EXPECT_EQ(1u, cookie_changes_1.size()); EXPECT_EQ(1u, cookie_changes_2.size()); EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "abc=ghi; path=/foo")); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes_1.size()); EXPECT_EQ("abc", cookie_changes_1[0].cookie.Name()); EXPECT_EQ("def", cookie_changes_1[0].cookie.Value()); EXPECT_EQ("/", cookie_changes_1[0].cookie.Path()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes_1[0].cookie.Domain()); ASSERT_LE(1u, cookie_changes_2.size()); EXPECT_EQ("abc", cookie_changes_2[0].cookie.Name()); EXPECT_EQ("def", cookie_changes_2[0].cookie.Value()); EXPECT_EQ("/", cookie_changes_2[0].cookie.Path()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes_2[0].cookie.Domain()); ASSERT_LE(2u, cookie_changes_2.size()); EXPECT_EQ("abc", cookie_changes_2[1].cookie.Name()); EXPECT_EQ("ghi", cookie_changes_2[1].cookie.Value()); EXPECT_EQ("/foo", cookie_changes_2[1].cookie.Path()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes_2[1].cookie.Domain()); EXPECT_EQ(2u, cookie_changes_2.size()); } TYPED_TEST_P(CookieStoreChangeNamedTest, DifferentSubscriptionsFiltering) { if (!TypeParam::supports_named_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes_1, cookie_changes_2; std::vector cookie_changes_3, cookie_changes_4; std::unique_ptr subscription1 = cs->GetChangeDispatcher().AddCallbackForCookie( this->http_www_foo_.url(), "abc", base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_1))); std::unique_ptr subscription2 = cs->GetChangeDispatcher().AddCallbackForCookie( this->http_www_foo_.url(), "hij", base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_2))); std::unique_ptr subscription3 = cs->GetChangeDispatcher().AddCallbackForCookie( this->http_bar_com_.url(), "abc", base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_3))); std::unique_ptr subscription4 = cs->GetChangeDispatcher().AddCallbackForCookie( this->www_foo_foo_.url(), "abc", base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_4))); this->DeliverChangeNotifications(); ASSERT_EQ(0u, cookie_changes_1.size()); ASSERT_EQ(0u, cookie_changes_2.size()); EXPECT_EQ(0u, cookie_changes_3.size()); EXPECT_EQ(0u, cookie_changes_4.size()); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "abc=def")); this->DeliverChangeNotifications(); EXPECT_EQ(1u, cookie_changes_1.size()); EXPECT_EQ(0u, cookie_changes_2.size()); EXPECT_EQ(0u, cookie_changes_3.size()); EXPECT_EQ(1u, cookie_changes_4.size()); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "xyz=zyx")); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "hij=mno")); this->DeliverChangeNotifications(); EXPECT_EQ(1u, cookie_changes_1.size()); EXPECT_EQ(1u, cookie_changes_2.size()); EXPECT_EQ(0u, cookie_changes_3.size()); EXPECT_EQ(1u, cookie_changes_4.size()); EXPECT_TRUE(this->SetCookie(cs, this->http_bar_com_.url(), "hij=pqr")); EXPECT_TRUE(this->SetCookie(cs, this->http_bar_com_.url(), "xyz=zyx")); EXPECT_TRUE(this->SetCookie(cs, this->http_bar_com_.url(), "abc=stu")); this->DeliverChangeNotifications(); EXPECT_EQ(1u, cookie_changes_1.size()); EXPECT_EQ(1u, cookie_changes_2.size()); EXPECT_EQ(1u, cookie_changes_3.size()); EXPECT_EQ(1u, cookie_changes_4.size()); EXPECT_TRUE( this->SetCookie(cs, this->http_www_foo_.url(), "abc=vwx; path=/foo")); this->DeliverChangeNotifications(); ASSERT_LE(1u, cookie_changes_1.size()); EXPECT_EQ("abc", cookie_changes_1[0].cookie.Name()); EXPECT_EQ("def", cookie_changes_1[0].cookie.Value()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes_1[0].cookie.Domain()); EXPECT_EQ(1u, cookie_changes_1.size()); ASSERT_LE(1u, cookie_changes_2.size()); EXPECT_EQ("hij", cookie_changes_2[0].cookie.Name()); EXPECT_EQ("mno", cookie_changes_2[0].cookie.Value()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes_2[0].cookie.Domain()); EXPECT_EQ(1u, cookie_changes_2.size()); ASSERT_LE(1u, cookie_changes_3.size()); EXPECT_EQ("abc", cookie_changes_3[0].cookie.Name()); EXPECT_EQ("stu", cookie_changes_3[0].cookie.Value()); EXPECT_EQ(this->http_bar_com_.url().host(), cookie_changes_3[0].cookie.Domain()); EXPECT_EQ(1u, cookie_changes_3.size()); ASSERT_LE(1u, cookie_changes_4.size()); EXPECT_EQ("abc", cookie_changes_4[0].cookie.Name()); EXPECT_EQ("def", cookie_changes_4[0].cookie.Value()); EXPECT_EQ("/", cookie_changes_4[0].cookie.Path()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes_4[0].cookie.Domain()); ASSERT_LE(2u, cookie_changes_4.size()); EXPECT_EQ("abc", cookie_changes_4[1].cookie.Name()); EXPECT_EQ("vwx", cookie_changes_4[1].cookie.Value()); EXPECT_EQ("/foo", cookie_changes_4[1].cookie.Path()); EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes_4[1].cookie.Domain()); EXPECT_EQ(2u, cookie_changes_4.size()); } TYPED_TEST_P(CookieStoreChangeNamedTest, MultipleSubscriptions) { if (!TypeParam::supports_named_cookie_tracking || !TypeParam::supports_multiple_tracking_callbacks) return; CookieStore* cs = this->GetCookieStore(); std::vector cookie_changes_1, cookie_changes_2; std::unique_ptr subscription1 = cs->GetChangeDispatcher().AddCallbackForCookie( this->http_www_foo_.url(), "abc", base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_1))); std::unique_ptr subscription2 = cs->GetChangeDispatcher().AddCallbackForCookie( this->http_www_foo_.url(), "abc", base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes_2))); this->DeliverChangeNotifications(); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "xyz=zyx")); EXPECT_TRUE(this->SetCookie(cs, this->http_www_foo_.url(), "abc=def")); this->DeliverChangeNotifications(); ASSERT_EQ(1U, cookie_changes_1.size()); EXPECT_EQ("abc", cookie_changes_1[0].cookie.Name()); EXPECT_EQ("def", cookie_changes_1[0].cookie.Value()); cookie_changes_1.clear(); ASSERT_EQ(1U, cookie_changes_2.size()); EXPECT_EQ("abc", cookie_changes_2[0].cookie.Name()); EXPECT_EQ("def", cookie_changes_2[0].cookie.Value()); cookie_changes_2.clear(); } TYPED_TEST_P(CookieStoreChangeNamedTest, SubscriptionOutlivesStore) { if (!TypeParam::supports_named_cookie_tracking) return; std::vector cookie_changes; std::unique_ptr subscription = this->GetCookieStore()->GetChangeDispatcher().AddCallbackForCookie( this->http_www_foo_.url(), "abc", base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); this->ResetCookieStore(); // |subscription| outlives cookie_store - crash should not happen. subscription.reset(); } TYPED_TEST_P(CookieStoreChangeNamedTest, ChangeIncludesCookieAccessSemantics) { if (!TypeParam::supports_named_cookie_tracking) return; CookieStore* cs = this->GetCookieStore(); // if !supports_cookie_access_semantics, the delegate will be stored but will // not be used. auto access_delegate = std::make_unique(); access_delegate->SetExpectationForCookieDomain("domain1.test", CookieAccessSemantics::LEGACY); cs->SetCookieAccessDelegate(std::move(access_delegate)); std::vector cookie_changes; std::unique_ptr subscription = cs->GetChangeDispatcher().AddCallbackForCookie( GURL("http://domain1.test"), "cookie", base::BindRepeating( &CookieStoreChangeTestBase::OnCookieChange, base::Unretained(&cookie_changes))); this->CreateAndSetCookie(cs, GURL("http://domain1.test"), "cookie=1", CookieOptions::MakeAllInclusive()); this->DeliverChangeNotifications(); ASSERT_EQ(1u, cookie_changes.size()); EXPECT_EQ("domain1.test", cookie_changes[0].cookie.Domain()); EXPECT_EQ("cookie", cookie_changes[0].cookie.Name()); EXPECT_TRUE(this->IsExpectedAccessSemantics( CookieAccessSemantics::LEGACY, cookie_changes[0].access_semantics)); } REGISTER_TYPED_TEST_SUITE_P(CookieStoreChangeGlobalTest, NoCookie, InitialCookie, InsertOne, InsertMany, DeleteOne, DeleteTwo, Overwrite, OverwriteWithHttpOnly, Deregister, DeregisterMultiple, DispatchRace, DeregisterRace, DeregisterRaceMultiple, MultipleSubscriptions, ChangeIncludesCookieAccessSemantics); REGISTER_TYPED_TEST_SUITE_P(CookieStoreChangeUrlTest, NoCookie, InitialCookie, InsertOne, InsertMany, InsertFiltering, DeleteOne, DeleteTwo, DeleteFiltering, Overwrite, OverwriteFiltering, OverwriteWithHttpOnly, Deregister, DeregisterMultiple, DispatchRace, DeregisterRace, DeregisterRaceMultiple, DifferentSubscriptionsDisjoint, DifferentSubscriptionsDomains, DifferentSubscriptionsPaths, DifferentSubscriptionsFiltering, MultipleSubscriptions, ChangeIncludesCookieAccessSemantics); REGISTER_TYPED_TEST_SUITE_P(CookieStoreChangeNamedTest, NoCookie, InitialCookie, InsertOne, InsertTwo, InsertFiltering, DeleteOne, DeleteTwo, DeleteFiltering, Overwrite, OverwriteFiltering, OverwriteWithHttpOnly, Deregister, DeregisterMultiple, DispatchRace, DeregisterRace, DeregisterRaceMultiple, DifferentSubscriptionsDisjoint, DifferentSubscriptionsDomains, DifferentSubscriptionsNames, DifferentSubscriptionsPaths, DifferentSubscriptionsFiltering, MultipleSubscriptions, SubscriptionOutlivesStore, ChangeIncludesCookieAccessSemantics); } // namespace net #endif // NET_COOKIES_COOKIE_STORE_CHANGE_UNITTEST_H