blob: b1e202dd0c5a2850662f2a2bbc7a944ed0cddf97 [file] [log] [blame]
// 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_store.h"
#include "net/cookies/cookie_store_unittest.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace net {
namespace {
using CookieChange = std::pair<CanonicalCookie, CookieChangeCause>;
// 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 CookieChange& lhs, const CookieChange& rhs) {
if (lhs.first.Name() != rhs.first.Name())
return lhs.first.Name() < rhs.first.Name();
if (lhs.first.Value() != rhs.first.Value())
return lhs.first.Value() < rhs.first.Value();
if (lhs.first.Domain() != rhs.first.Domain())
return lhs.first.Domain() < rhs.first.Domain();
return lhs.second < rhs.second;
}
} // namespace
// Google Test supports at most 50 tests per typed case, so the tests here are
// broken up into multiple cases.
template <class CookieStoreTestTraits>
class CookieStoreChangeTestBase
: public CookieStoreTest<CookieStoreTestTraits> {
protected:
using CookieStoreTest<CookieStoreTestTraits>::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;
}
static void OnCookieChange(std::vector<CookieChange>* changes,
const CanonicalCookie& cookie,
CookieChangeCause cause) {
CookieChange notification(cookie, cause);
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 CookieStoreTestTraits>
class CookieStoreChangeGlobalTest
: public CookieStoreChangeTestBase<CookieStoreTestTraits> {};
TYPED_TEST_SUITE_P(CookieStoreChangeGlobalTest);
template <class CookieStoreTestTraits>
class CookieStoreChangeUrlTest
: public CookieStoreChangeTestBase<CookieStoreTestTraits> {};
TYPED_TEST_SUITE_P(CookieStoreChangeUrlTest);
template <class CookieStoreTestTraits>
class CookieStoreChangeNamedTest
: public CookieStoreChangeTestBase<CookieStoreTestTraits> {};
TYPED_TEST_SUITE_P(CookieStoreChangeNamedTest);
TYPED_TEST_P(CookieStoreChangeGlobalTest, NoCookie) {
if (!TypeParam::supports_global_cookie_tracking)
return;
CookieStore* cs = this->GetCookieStore();
std::vector<CookieChange> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForAllChanges(base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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<CookieChange> cookie_changes;
this->SetCookie(cs, this->http_www_foo_.url(), "A=B");
this->DeliverChangeNotifications();
std::unique_ptr<CookieChangeSubscription> subscription(
cs->GetChangeDispatcher().AddCallbackForAllChanges(base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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<CookieChange> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForAllChanges(base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].second));
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].first.Domain());
EXPECT_EQ("A", cookie_changes[0].first.Name());
EXPECT_EQ("B", cookie_changes[0].first.Value());
}
TYPED_TEST_P(CookieStoreChangeGlobalTest, InsertMany) {
if (!TypeParam::supports_global_cookie_tracking)
return;
CookieStore* cs = this->GetCookieStore();
std::vector<CookieChange> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForAllChanges(base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].second));
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].first.Domain());
EXPECT_EQ("A", cookie_changes[0].first.Name());
EXPECT_EQ("B", cookie_changes[0].first.Value());
ASSERT_LE(2u, cookie_changes.size());
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[1].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::INSERTED,
cookie_changes[1].second));
EXPECT_EQ("C", cookie_changes[1].first.Name());
EXPECT_EQ("D", cookie_changes[1].first.Value());
ASSERT_LE(3u, cookie_changes.size());
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[2].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::INSERTED,
cookie_changes[2].second));
EXPECT_EQ("E", cookie_changes[2].first.Name());
EXPECT_EQ("F", cookie_changes[2].first.Value());
ASSERT_LE(4u, cookie_changes.size());
EXPECT_EQ(this->http_bar_com_.url().host(), cookie_changes[3].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::INSERTED,
cookie_changes[3].second));
EXPECT_EQ("G", cookie_changes[3].first.Name());
EXPECT_EQ("H", cookie_changes[3].first.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<CookieChange> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForAllChanges(base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::EXPLICIT,
cookie_changes[0].second));
EXPECT_EQ("A", cookie_changes[0].first.Name());
EXPECT_EQ("B", cookie_changes[0].first.Value());
}
TYPED_TEST_P(CookieStoreChangeGlobalTest, DeleteTwo) {
if (!TypeParam::supports_global_cookie_tracking)
return;
CookieStore* cs = this->GetCookieStore();
std::vector<CookieChange> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForAllChanges(base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::EXPLICIT,
cookie_changes[0].second));
EXPECT_EQ("C", cookie_changes[0].first.Name());
EXPECT_EQ("D", cookie_changes[0].first.Value());
ASSERT_EQ(2u, cookie_changes.size());
EXPECT_EQ(this->http_bar_com_.url().host(), cookie_changes[1].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::EXPLICIT,
cookie_changes[1].second));
EXPECT_EQ("G", cookie_changes[1].first.Name());
EXPECT_EQ("H", cookie_changes[1].first.Value());
}
TYPED_TEST_P(CookieStoreChangeGlobalTest, Overwrite) {
if (!TypeParam::supports_global_cookie_tracking)
return;
CookieStore* cs = this->GetCookieStore();
std::vector<CookieChange> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForAllChanges(base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::OVERWRITE,
cookie_changes[0].second));
EXPECT_EQ("A", cookie_changes[0].first.Name());
EXPECT_EQ("B", cookie_changes[0].first.Value());
ASSERT_LE(2u, cookie_changes.size());
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[1].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::INSERTED,
cookie_changes[1].second));
EXPECT_EQ("A", cookie_changes[1].first.Name());
EXPECT_EQ("C", cookie_changes[1].first.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<CookieChange> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForAllChanges(base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].second));
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].first.Domain());
EXPECT_EQ("A", cookie_changes[0].first.Name());
EXPECT_EQ("B", cookie_changes[0].first.Value());
EXPECT_FALSE(cookie_changes[0].first.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();
EXPECT_TRUE(this->SetCookieWithOptions(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].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::OVERWRITE,
cookie_changes[0].second));
EXPECT_EQ("A", cookie_changes[0].first.Name());
EXPECT_EQ("B", cookie_changes[0].first.Value());
EXPECT_FALSE(cookie_changes[0].first.IsHttpOnly());
ASSERT_LE(2u, cookie_changes.size());
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[1].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::INSERTED,
cookie_changes[1].second));
EXPECT_EQ("A", cookie_changes[1].first.Name());
EXPECT_EQ("C", cookie_changes[1].first.Value());
EXPECT_TRUE(cookie_changes[1].first.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<CookieChange> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForAllChanges(base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Name());
EXPECT_EQ("B", cookie_changes[0].first.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<CookieChange> cookie_changes_1;
std::unique_ptr<CookieChangeSubscription> subscription1 =
cs->GetChangeDispatcher().AddCallbackForAllChanges(base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_1)));
std::vector<CookieChange> cookie_changes_2;
std::unique_ptr<CookieChangeSubscription> subscription2 =
cs->GetChangeDispatcher().AddCallbackForAllChanges(base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Name());
EXPECT_EQ("B", cookie_changes_1[0].first.Value());
cookie_changes_1.clear();
ASSERT_EQ(1u, cookie_changes_2.size());
EXPECT_EQ("A", cookie_changes_2[0].first.Name());
EXPECT_EQ("B", cookie_changes_2[0].first.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].first.Name());
EXPECT_EQ("D", cookie_changes_1[0].first.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<CookieChange> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForAllChanges(base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Name());
EXPECT_EQ("D", cookie_changes[0].first.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<CookieChange> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForAllChanges(base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Name());
EXPECT_EQ("B", cookie_changes[0].first.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<CookieChange> cookie_changes_1, cookie_changes_2;
std::unique_ptr<CookieChangeSubscription> subscription1 =
cs->GetChangeDispatcher().AddCallbackForAllChanges(base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_1)));
std::unique_ptr<CookieChangeSubscription> subscription2 =
cs->GetChangeDispatcher().AddCallbackForAllChanges(base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Name());
EXPECT_EQ("B", cookie_changes_1[0].first.Value());
cookie_changes_1.clear();
ASSERT_EQ(1u, cookie_changes_2.size());
EXPECT_EQ("A", cookie_changes_2[0].first.Name());
EXPECT_EQ("B", cookie_changes_2[0].first.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].first.Name());
EXPECT_EQ("D", cookie_changes_1[0].first.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<CookieChange> cookie_changes_1, cookie_changes_2;
std::unique_ptr<CookieChangeSubscription> subscription1 =
cs->GetChangeDispatcher().AddCallbackForAllChanges(base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_1)));
std::unique_ptr<CookieChangeSubscription> subscription2 =
cs->GetChangeDispatcher().AddCallbackForAllChanges(base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Name());
EXPECT_EQ("B", cookie_changes_1[0].first.Value());
ASSERT_EQ(1U, cookie_changes_2.size());
EXPECT_EQ("A", cookie_changes_2[0].first.Name());
EXPECT_EQ("B", cookie_changes_2[0].first.Value());
}
TYPED_TEST_P(CookieStoreChangeUrlTest, NoCookie) {
if (!TypeParam::supports_url_cookie_tracking)
return;
CookieStore* cs = this->GetCookieStore();
std::vector<CookieChange> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForUrl(
this->http_www_foo_.url(),
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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<CookieChange> cookie_changes;
this->SetCookie(cs, this->http_www_foo_.url(), "A=B");
this->DeliverChangeNotifications();
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForUrl(
this->http_www_foo_.url(),
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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<CookieChange> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForUrl(
this->http_www_foo_.url(),
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Name());
EXPECT_EQ("B", cookie_changes[0].first.Value());
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::INSERTED,
cookie_changes[0].second));
}
TYPED_TEST_P(CookieStoreChangeUrlTest, InsertMany) {
if (!TypeParam::supports_url_cookie_tracking)
return;
CookieStore* cs = this->GetCookieStore();
std::vector<CookieChange> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForUrl(
this->http_www_foo_.url(),
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].second));
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].first.Domain());
EXPECT_EQ("A", cookie_changes[0].first.Name());
EXPECT_EQ("B", cookie_changes[0].first.Value());
ASSERT_LE(2u, cookie_changes.size());
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[1].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::INSERTED,
cookie_changes[1].second));
EXPECT_EQ("C", cookie_changes[1].first.Name());
EXPECT_EQ("D", cookie_changes[1].first.Value());
ASSERT_LE(3u, cookie_changes.size());
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[2].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::INSERTED,
cookie_changes[2].second));
EXPECT_EQ("E", cookie_changes[2].first.Name());
EXPECT_EQ("F", cookie_changes[2].first.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<CookieChange> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForUrl(
this->www_foo_foo_.url(),
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Name());
EXPECT_EQ("B", cookie_changes[0].first.Value());
EXPECT_EQ("/", cookie_changes[0].first.Path());
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::INSERTED,
cookie_changes[0].second));
ASSERT_LE(2u, cookie_changes.size());
EXPECT_EQ("I", cookie_changes[1].first.Name());
EXPECT_EQ("J", cookie_changes[1].first.Value());
EXPECT_EQ("/foo", cookie_changes[1].first.Path());
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[1].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::INSERTED,
cookie_changes[1].second));
ASSERT_LE(3u, cookie_changes.size());
EXPECT_EQ("K", cookie_changes[2].first.Name());
EXPECT_EQ("L", cookie_changes[2].first.Value());
EXPECT_EQ("/", cookie_changes[2].first.Path());
EXPECT_EQ(".foo.com", cookie_changes[2].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::INSERTED,
cookie_changes[2].second));
EXPECT_EQ(3u, cookie_changes.size());
}
TYPED_TEST_P(CookieStoreChangeUrlTest, DeleteOne) {
if (!TypeParam::supports_url_cookie_tracking)
return;
CookieStore* cs = this->GetCookieStore();
std::vector<CookieChange> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForUrl(
this->http_www_foo_.url(),
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Name());
EXPECT_EQ("B", cookie_changes[0].first.Value());
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].first.Domain());
ASSERT_TRUE(this->MatchesCause(CookieChangeCause::EXPLICIT,
cookie_changes[0].second));
}
TYPED_TEST_P(CookieStoreChangeUrlTest, DeleteTwo) {
if (!TypeParam::supports_url_cookie_tracking)
return;
CookieStore* cs = this->GetCookieStore();
std::vector<CookieChange> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForUrl(
this->http_www_foo_.url(),
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::EXPLICIT,
cookie_changes[0].second));
EXPECT_EQ("C", cookie_changes[0].first.Name());
EXPECT_EQ("D", cookie_changes[0].first.Value());
ASSERT_EQ(2u, cookie_changes.size());
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[1].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::EXPLICIT,
cookie_changes[1].second));
EXPECT_EQ("G", cookie_changes[1].first.Name());
EXPECT_EQ("H", cookie_changes[1].first.Value());
}
TYPED_TEST_P(CookieStoreChangeUrlTest, DeleteFiltering) {
if (!TypeParam::supports_url_cookie_tracking)
return;
CookieStore* cs = this->GetCookieStore();
std::vector<CookieChange> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForUrl(
this->www_foo_foo_.url(),
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Name());
EXPECT_EQ("B", cookie_changes[0].first.Value());
EXPECT_EQ("/", cookie_changes[0].first.Path());
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::EXPLICIT,
cookie_changes[0].second));
ASSERT_LE(2u, cookie_changes.size());
EXPECT_EQ("I", cookie_changes[1].first.Name());
EXPECT_EQ("J", cookie_changes[1].first.Value());
EXPECT_EQ("/foo", cookie_changes[1].first.Path());
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[1].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::EXPLICIT,
cookie_changes[1].second));
ASSERT_LE(3u, cookie_changes.size());
EXPECT_EQ("K", cookie_changes[2].first.Name());
EXPECT_EQ("L", cookie_changes[2].first.Value());
EXPECT_EQ("/", cookie_changes[2].first.Path());
EXPECT_EQ(".foo.com", cookie_changes[2].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::EXPLICIT,
cookie_changes[2].second));
EXPECT_EQ(3u, cookie_changes.size());
}
TYPED_TEST_P(CookieStoreChangeUrlTest, Overwrite) {
if (!TypeParam::supports_url_cookie_tracking)
return;
CookieStore* cs = this->GetCookieStore();
std::vector<CookieChange> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForUrl(
this->http_www_foo_.url(),
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::OVERWRITE,
cookie_changes[0].second));
EXPECT_EQ("A", cookie_changes[0].first.Name());
EXPECT_EQ("B", cookie_changes[0].first.Value());
ASSERT_LE(2u, cookie_changes.size());
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[1].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::INSERTED,
cookie_changes[1].second));
EXPECT_EQ("A", cookie_changes[1].first.Name());
EXPECT_EQ("C", cookie_changes[1].first.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<CookieChange> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForUrl(
this->www_foo_foo_.url(),
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Name());
EXPECT_EQ("B", cookie_changes[0].first.Value());
EXPECT_EQ("/", cookie_changes[0].first.Path());
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::OVERWRITE,
cookie_changes[0].second));
ASSERT_LE(2u, cookie_changes.size());
EXPECT_EQ("A", cookie_changes[1].first.Name());
EXPECT_EQ("b", cookie_changes[1].first.Value());
EXPECT_EQ("/", cookie_changes[1].first.Path());
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[1].first.Domain());
EXPECT_EQ(CookieChangeCause::INSERTED, cookie_changes[1].second);
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::INSERTED,
cookie_changes[1].second));
ASSERT_LE(3u, cookie_changes.size());
EXPECT_EQ("I", cookie_changes[2].first.Name());
EXPECT_EQ("J", cookie_changes[2].first.Value());
EXPECT_EQ("/foo", cookie_changes[2].first.Path());
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[2].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::OVERWRITE,
cookie_changes[2].second));
ASSERT_LE(4u, cookie_changes.size());
EXPECT_EQ("I", cookie_changes[3].first.Name());
EXPECT_EQ("j", cookie_changes[3].first.Value());
EXPECT_EQ("/foo", cookie_changes[3].first.Path());
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[3].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::INSERTED,
cookie_changes[3].second));
ASSERT_LE(5u, cookie_changes.size());
EXPECT_EQ("K", cookie_changes[4].first.Name());
EXPECT_EQ("L", cookie_changes[4].first.Value());
EXPECT_EQ("/", cookie_changes[4].first.Path());
EXPECT_EQ(".foo.com", cookie_changes[4].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::OVERWRITE,
cookie_changes[4].second));
ASSERT_LE(6u, cookie_changes.size());
EXPECT_EQ("K", cookie_changes[5].first.Name());
EXPECT_EQ("l", cookie_changes[5].first.Value());
EXPECT_EQ("/", cookie_changes[5].first.Path());
EXPECT_EQ(".foo.com", cookie_changes[5].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::INSERTED,
cookie_changes[5].second));
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<CookieChange> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForUrl(
this->www_foo_foo_.url(),
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].second));
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].first.Domain());
EXPECT_EQ("A", cookie_changes[0].first.Name());
EXPECT_EQ("B", cookie_changes[0].first.Value());
EXPECT_FALSE(cookie_changes[0].first.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();
EXPECT_TRUE(this->SetCookieWithOptions(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].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::OVERWRITE,
cookie_changes[0].second));
EXPECT_EQ("A", cookie_changes[0].first.Name());
EXPECT_EQ("B", cookie_changes[0].first.Value());
EXPECT_FALSE(cookie_changes[0].first.IsHttpOnly());
ASSERT_LE(2u, cookie_changes.size());
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[1].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::INSERTED,
cookie_changes[1].second));
EXPECT_EQ("A", cookie_changes[1].first.Name());
EXPECT_EQ("C", cookie_changes[1].first.Value());
EXPECT_TRUE(cookie_changes[1].first.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<CookieChange> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForUrl(
this->http_www_foo_.url(),
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Name());
EXPECT_EQ("B", cookie_changes[0].first.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<CookieChange> cookie_changes_1, cookie_changes_2;
std::unique_ptr<CookieChangeSubscription> subscription1 =
cs->GetChangeDispatcher().AddCallbackForUrl(
this->http_www_foo_.url(),
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_1)));
std::unique_ptr<CookieChangeSubscription> subscription2 =
cs->GetChangeDispatcher().AddCallbackForUrl(
this->http_www_foo_.url(),
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Name());
EXPECT_EQ("B", cookie_changes_1[0].first.Value());
cookie_changes_1.clear();
ASSERT_EQ(1u, cookie_changes_2.size());
EXPECT_EQ("A", cookie_changes_2[0].first.Name());
EXPECT_EQ("B", cookie_changes_2[0].first.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].first.Name());
EXPECT_EQ("D", cookie_changes_1[0].first.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<CookieChange> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForUrl(
this->http_www_foo_.url(),
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Name());
EXPECT_EQ("D", cookie_changes[0].first.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<CookieChange> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForUrl(
this->http_www_foo_.url(),
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Name());
EXPECT_EQ("B", cookie_changes[0].first.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<CookieChange> cookie_changes_1, cookie_changes_2;
std::unique_ptr<CookieChangeSubscription> subscription1 =
cs->GetChangeDispatcher().AddCallbackForUrl(
this->http_www_foo_.url(),
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_1)));
std::unique_ptr<CookieChangeSubscription> subscription2 =
cs->GetChangeDispatcher().AddCallbackForUrl(
this->http_www_foo_.url(),
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Name());
EXPECT_EQ("B", cookie_changes_1[0].first.Value());
cookie_changes_1.clear();
ASSERT_EQ(1u, cookie_changes_2.size());
EXPECT_EQ("A", cookie_changes_2[0].first.Name());
EXPECT_EQ("B", cookie_changes_2[0].first.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].first.Name());
EXPECT_EQ("D", cookie_changes_1[0].first.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<CookieChange> cookie_changes_1, cookie_changes_2;
std::unique_ptr<CookieChangeSubscription> subscription1 =
cs->GetChangeDispatcher().AddCallbackForUrl(
this->http_www_foo_.url(),
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_1)));
std::unique_ptr<CookieChangeSubscription> subscription2 =
cs->GetChangeDispatcher().AddCallbackForUrl(
this->http_bar_com_.url(),
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Name());
EXPECT_EQ("B", cookie_changes_1[0].first.Value());
EXPECT_EQ(this->http_www_foo_.url().host(),
cookie_changes_1[0].first.Domain());
ASSERT_EQ(1u, cookie_changes_2.size());
EXPECT_EQ("C", cookie_changes_2[0].first.Name());
EXPECT_EQ("D", cookie_changes_2[0].first.Value());
EXPECT_EQ(this->http_bar_com_.url().host(),
cookie_changes_2[0].first.Domain());
}
TYPED_TEST_P(CookieStoreChangeUrlTest, DifferentSubscriptionsDomains) {
if (!TypeParam::supports_url_cookie_tracking)
return;
CookieStore* cs = this->GetCookieStore();
std::vector<CookieChange> cookie_changes_1, cookie_changes_2;
std::unique_ptr<CookieChangeSubscription> subscription1 =
cs->GetChangeDispatcher().AddCallbackForUrl(
this->http_www_foo_.url(),
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_1)));
std::unique_ptr<CookieChangeSubscription> subscription2 =
cs->GetChangeDispatcher().AddCallbackForUrl(
this->http_bar_com_.url(),
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Name());
EXPECT_EQ("B", cookie_changes_1[0].first.Value());
EXPECT_EQ(this->http_www_foo_.url().host(),
cookie_changes_1[0].first.Domain());
ASSERT_EQ(1u, cookie_changes_2.size());
EXPECT_EQ("C", cookie_changes_2[0].first.Name());
EXPECT_EQ("D", cookie_changes_2[0].first.Value());
EXPECT_EQ(this->http_bar_com_.url().host(),
cookie_changes_2[0].first.Domain());
}
TYPED_TEST_P(CookieStoreChangeUrlTest, DifferentSubscriptionsPaths) {
if (!TypeParam::supports_url_cookie_tracking)
return;
CookieStore* cs = this->GetCookieStore();
std::vector<CookieChange> cookie_changes_1, cookie_changes_2;
std::unique_ptr<CookieChangeSubscription> subscription1 =
cs->GetChangeDispatcher().AddCallbackForUrl(
this->http_www_foo_.url(),
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_1)));
std::unique_ptr<CookieChangeSubscription> subscription2 =
cs->GetChangeDispatcher().AddCallbackForUrl(
this->www_foo_foo_.url(),
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Name());
EXPECT_EQ("B", cookie_changes_1[0].first.Value());
EXPECT_EQ("/", cookie_changes_1[0].first.Path());
EXPECT_EQ(this->http_www_foo_.url().host(),
cookie_changes_1[0].first.Domain());
ASSERT_LE(1u, cookie_changes_2.size());
EXPECT_EQ("A", cookie_changes_2[0].first.Name());
EXPECT_EQ("B", cookie_changes_2[0].first.Value());
EXPECT_EQ("/", cookie_changes_2[0].first.Path());
EXPECT_EQ(this->http_www_foo_.url().host(),
cookie_changes_2[0].first.Domain());
ASSERT_LE(2u, cookie_changes_2.size());
EXPECT_EQ("C", cookie_changes_2[1].first.Name());
EXPECT_EQ("D", cookie_changes_2[1].first.Value());
EXPECT_EQ("/foo", cookie_changes_2[1].first.Path());
EXPECT_EQ(this->http_www_foo_.url().host(),
cookie_changes_2[1].first.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<CookieChange> cookie_changes_1, cookie_changes_2;
std::vector<CookieChange> cookie_changes_3;
std::unique_ptr<CookieChangeSubscription> subscription1 =
cs->GetChangeDispatcher().AddCallbackForUrl(
this->http_www_foo_.url(),
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_1)));
std::unique_ptr<CookieChangeSubscription> subscription2 =
cs->GetChangeDispatcher().AddCallbackForUrl(
this->http_bar_com_.url(),
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_2)));
std::unique_ptr<CookieChangeSubscription> subscription3 =
cs->GetChangeDispatcher().AddCallbackForUrl(
this->www_foo_foo_.url(),
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Name());
EXPECT_EQ("B", cookie_changes_1[0].first.Value());
EXPECT_EQ(this->http_www_foo_.url().host(),
cookie_changes_1[0].first.Domain());
EXPECT_EQ(1u, cookie_changes_1.size());
ASSERT_LE(1u, cookie_changes_2.size());
EXPECT_EQ("C", cookie_changes_2[0].first.Name());
EXPECT_EQ("D", cookie_changes_2[0].first.Value());
EXPECT_EQ(this->http_bar_com_.url().host(),
cookie_changes_2[0].first.Domain());
EXPECT_EQ(1u, cookie_changes_2.size());
ASSERT_LE(1u, cookie_changes_3.size());
EXPECT_EQ("A", cookie_changes_3[0].first.Name());
EXPECT_EQ("B", cookie_changes_3[0].first.Value());
EXPECT_EQ("/", cookie_changes_3[0].first.Path());
EXPECT_EQ(this->http_www_foo_.url().host(),
cookie_changes_3[0].first.Domain());
ASSERT_LE(2u, cookie_changes_3.size());
EXPECT_EQ("E", cookie_changes_3[1].first.Name());
EXPECT_EQ("F", cookie_changes_3[1].first.Value());
EXPECT_EQ("/foo", cookie_changes_3[1].first.Path());
EXPECT_EQ(this->http_www_foo_.url().host(),
cookie_changes_3[1].first.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<CookieChange> cookie_changes_1, cookie_changes_2;
std::unique_ptr<CookieChangeSubscription> subscription1 =
cs->GetChangeDispatcher().AddCallbackForUrl(
this->http_www_foo_.url(),
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_1)));
std::unique_ptr<CookieChangeSubscription> subscription2 =
cs->GetChangeDispatcher().AddCallbackForUrl(
this->http_www_foo_.url(),
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Name());
EXPECT_EQ("B", cookie_changes_1[0].first.Value());
ASSERT_EQ(1U, cookie_changes_2.size());
EXPECT_EQ("A", cookie_changes_2[0].first.Name());
EXPECT_EQ("B", cookie_changes_2[0].first.Value());
}
TYPED_TEST_P(CookieStoreChangeNamedTest, NoCookie) {
if (!TypeParam::supports_named_cookie_tracking)
return;
CookieStore* cs = this->GetCookieStore();
std::vector<CookieChange> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->http_www_foo_.url(), "abc",
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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<CookieChange> cookie_changes;
this->SetCookie(cs, this->http_www_foo_.url(), "abc=def");
this->DeliverChangeNotifications();
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->http_www_foo_.url(), "abc",
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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<CookieChange> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->http_www_foo_.url(), "abc",
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Name());
EXPECT_EQ("def", cookie_changes[0].first.Value());
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::INSERTED,
cookie_changes[0].second));
}
TYPED_TEST_P(CookieStoreChangeNamedTest, InsertTwo) {
if (!TypeParam::supports_named_cookie_tracking)
return;
CookieStore* cs = this->GetCookieStore();
std::vector<CookieChange> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->www_foo_foo_.url(), "abc",
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Name());
EXPECT_EQ("def", cookie_changes[0].first.Value());
EXPECT_EQ("/", cookie_changes[0].first.Path());
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::INSERTED,
cookie_changes[0].second));
ASSERT_LE(2u, cookie_changes.size());
EXPECT_EQ("abc", cookie_changes[1].first.Name());
EXPECT_EQ("hij", cookie_changes[1].first.Value());
EXPECT_EQ("/foo", cookie_changes[1].first.Path());
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[1].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::INSERTED,
cookie_changes[1].second));
EXPECT_EQ(2u, cookie_changes.size());
}
TYPED_TEST_P(CookieStoreChangeNamedTest, InsertFiltering) {
if (!TypeParam::supports_named_cookie_tracking)
return;
CookieStore* cs = this->GetCookieStore();
std::vector<CookieChange> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->www_foo_foo_.url(), "abc",
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Name());
EXPECT_EQ("def", cookie_changes[0].first.Value());
EXPECT_EQ("/", cookie_changes[0].first.Path());
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::INSERTED,
cookie_changes[0].second));
ASSERT_LE(2u, cookie_changes.size());
EXPECT_EQ("abc", cookie_changes[1].first.Name());
EXPECT_EQ("pqr", cookie_changes[1].first.Value());
EXPECT_EQ("/foo", cookie_changes[1].first.Path());
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[1].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::INSERTED,
cookie_changes[1].second));
ASSERT_LE(3u, cookie_changes.size());
EXPECT_EQ("abc", cookie_changes[2].first.Name());
EXPECT_EQ("stu", cookie_changes[2].first.Value());
EXPECT_EQ("/", cookie_changes[2].first.Path());
EXPECT_EQ(".foo.com", cookie_changes[2].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::INSERTED,
cookie_changes[2].second));
EXPECT_EQ(3u, cookie_changes.size());
}
TYPED_TEST_P(CookieStoreChangeNamedTest, DeleteOne) {
if (!TypeParam::supports_named_cookie_tracking)
return;
CookieStore* cs = this->GetCookieStore();
std::vector<CookieChange> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->http_www_foo_.url(), "abc",
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Name());
EXPECT_EQ("def", cookie_changes[0].first.Value());
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::EXPLICIT,
cookie_changes[0].second));
}
TYPED_TEST_P(CookieStoreChangeNamedTest, DeleteTwo) {
if (!TypeParam::supports_named_cookie_tracking)
return;
CookieStore* cs = this->GetCookieStore();
std::vector<CookieChange> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->www_foo_foo_.url(), "abc",
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Name());
EXPECT_EQ("def", cookie_changes[0].first.Value());
EXPECT_EQ("/", cookie_changes[0].first.Path());
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::EXPLICIT,
cookie_changes[0].second));
ASSERT_EQ(2u, cookie_changes.size());
EXPECT_EQ("abc", cookie_changes[1].first.Name());
EXPECT_EQ("hij", cookie_changes[1].first.Value());
EXPECT_EQ("/foo", cookie_changes[1].first.Path());
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[1].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::EXPLICIT,
cookie_changes[1].second));
}
TYPED_TEST_P(CookieStoreChangeNamedTest, DeleteFiltering) {
if (!TypeParam::supports_named_cookie_tracking)
return;
CookieStore* cs = this->GetCookieStore();
std::vector<CookieChange> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->www_foo_foo_.url(), "abc",
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Name());
EXPECT_EQ("mno", cookie_changes[0].first.Value());
EXPECT_EQ("/foo", cookie_changes[0].first.Path());
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::EXPLICIT,
cookie_changes[0].second));
ASSERT_LE(2u, cookie_changes.size());
EXPECT_EQ("abc", cookie_changes[1].first.Name());
EXPECT_EQ("pqr", cookie_changes[1].first.Value());
EXPECT_EQ("/", cookie_changes[1].first.Path());
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[1].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::EXPLICIT,
cookie_changes[1].second));
ASSERT_LE(3u, cookie_changes.size());
EXPECT_EQ("abc", cookie_changes[2].first.Name());
EXPECT_EQ("stu", cookie_changes[2].first.Value());
EXPECT_EQ("/", cookie_changes[2].first.Path());
EXPECT_EQ(".foo.com", cookie_changes[2].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::EXPLICIT,
cookie_changes[2].second));
EXPECT_EQ(3u, cookie_changes.size());
}
TYPED_TEST_P(CookieStoreChangeNamedTest, Overwrite) {
if (!TypeParam::supports_named_cookie_tracking)
return;
CookieStore* cs = this->GetCookieStore();
std::vector<CookieChange> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->http_www_foo_.url(), "abc",
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Name());
EXPECT_EQ("def", cookie_changes[0].first.Value());
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::OVERWRITE,
cookie_changes[0].second));
EXPECT_LE(2u, cookie_changes.size());
EXPECT_EQ("abc", cookie_changes[1].first.Name());
EXPECT_EQ("ghi", cookie_changes[1].first.Value());
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[1].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::INSERTED,
cookie_changes[1].second));
EXPECT_EQ(2u, cookie_changes.size());
}
TYPED_TEST_P(CookieStoreChangeNamedTest, OverwriteFiltering) {
if (!TypeParam::supports_named_cookie_tracking)
return;
CookieStore* cs = this->GetCookieStore();
std::vector<CookieChange> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->www_foo_foo_.url(), "abc",
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Name());
EXPECT_EQ("mno1", cookie_changes[0].first.Value());
EXPECT_EQ("/foo", cookie_changes[0].first.Path());
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::OVERWRITE,
cookie_changes[0].second));
ASSERT_LE(2u, cookie_changes.size());
EXPECT_EQ("abc", cookie_changes[1].first.Name());
EXPECT_EQ("mno2", cookie_changes[1].first.Value());
EXPECT_EQ("/foo", cookie_changes[1].first.Path());
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[1].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::INSERTED,
cookie_changes[1].second));
ASSERT_LE(3u, cookie_changes.size());
EXPECT_EQ("abc", cookie_changes[2].first.Name());
EXPECT_EQ("pqr1", cookie_changes[2].first.Value());
EXPECT_EQ("/", cookie_changes[2].first.Path());
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[2].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::OVERWRITE,
cookie_changes[2].second));
ASSERT_LE(4u, cookie_changes.size());
EXPECT_EQ("abc", cookie_changes[3].first.Name());
EXPECT_EQ("pqr2", cookie_changes[3].first.Value());
EXPECT_EQ("/", cookie_changes[3].first.Path());
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[3].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::INSERTED,
cookie_changes[3].second));
ASSERT_LE(5u, cookie_changes.size());
EXPECT_EQ("abc", cookie_changes[4].first.Name());
EXPECT_EQ("stu1", cookie_changes[4].first.Value());
EXPECT_EQ("/", cookie_changes[4].first.Path());
EXPECT_EQ(".foo.com", cookie_changes[4].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::OVERWRITE,
cookie_changes[4].second));
ASSERT_LE(6u, cookie_changes.size());
EXPECT_EQ("abc", cookie_changes[5].first.Name());
EXPECT_EQ("stu2", cookie_changes[5].first.Value());
EXPECT_EQ("/", cookie_changes[5].first.Path());
EXPECT_EQ(".foo.com", cookie_changes[5].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::INSERTED,
cookie_changes[5].second));
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<CookieChange> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->www_foo_foo_.url(), "abc",
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].second));
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[0].first.Domain());
EXPECT_EQ("abc", cookie_changes[0].first.Name());
EXPECT_EQ("def", cookie_changes[0].first.Value());
EXPECT_FALSE(cookie_changes[0].first.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();
EXPECT_TRUE(this->SetCookieWithOptions(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].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::OVERWRITE,
cookie_changes[0].second));
EXPECT_EQ("abc", cookie_changes[0].first.Name());
EXPECT_EQ("def", cookie_changes[0].first.Value());
EXPECT_FALSE(cookie_changes[0].first.IsHttpOnly());
ASSERT_LE(2u, cookie_changes.size());
EXPECT_EQ(this->http_www_foo_.url().host(), cookie_changes[1].first.Domain());
EXPECT_TRUE(this->MatchesCause(CookieChangeCause::INSERTED,
cookie_changes[1].second));
EXPECT_EQ("abc", cookie_changes[1].first.Name());
EXPECT_EQ("hij", cookie_changes[1].first.Value());
EXPECT_TRUE(cookie_changes[1].first.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<CookieChange> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->www_foo_foo_.url(), "abc",
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Name());
EXPECT_EQ("def", cookie_changes[0].first.Value());
EXPECT_EQ("/foo", cookie_changes[0].first.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<CookieChange> cookie_changes_1, cookie_changes_2;
std::unique_ptr<CookieChangeSubscription> subscription1 =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->www_foo_foo_.url(), "abc",
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_1)));
std::unique_ptr<CookieChangeSubscription> subscription2 =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->www_foo_foo_.url(), "abc",
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Name());
EXPECT_EQ("def", cookie_changes_1[0].first.Value());
EXPECT_EQ("/foo", cookie_changes_1[0].first.Path());
cookie_changes_1.clear();
ASSERT_EQ(1u, cookie_changes_2.size());
EXPECT_EQ("abc", cookie_changes_2[0].first.Name());
EXPECT_EQ("def", cookie_changes_2[0].first.Value());
EXPECT_EQ("/foo", cookie_changes_2[0].first.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].first.Name());
EXPECT_EQ("hij", cookie_changes_1[0].first.Value());
EXPECT_EQ("/", cookie_changes_1[0].first.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<CookieChange> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->www_foo_foo_.url(), "abc",
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Name());
EXPECT_EQ("hij", cookie_changes[0].first.Value());
EXPECT_EQ("/", cookie_changes[0].first.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<CookieChange> cookie_changes;
std::unique_ptr<CookieChangeSubscription> subscription =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->www_foo_foo_.url(), "abc",
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Name());
EXPECT_EQ("def", cookie_changes[0].first.Value());
EXPECT_EQ("/foo", cookie_changes[0].first.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<CookieChange> cookie_changes_1, cookie_changes_2;
std::unique_ptr<CookieChangeSubscription> subscription1 =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->www_foo_foo_.url(), "abc",
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_1)));
std::unique_ptr<CookieChangeSubscription> subscription2 =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->www_foo_foo_.url(), "abc",
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::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].first.Name());
EXPECT_EQ("def", cookie_changes_1[0].first.Value());
EXPECT_EQ("/foo", cookie_changes_1[0].first.Path());
cookie_changes_1.clear();
ASSERT_EQ(1u, cookie_changes_2.size());
EXPECT_EQ("abc", cookie_changes_2[0].first.Name());
EXPECT_EQ("def", cookie_changes_2[0].first.Value());
EXPECT_EQ("/foo", cookie_changes_2[0].first.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].first.Name());
EXPECT_EQ("hij", cookie_changes_1[0].first.Value());
EXPECT_EQ("/", cookie_changes_1[0].first.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<CookieChange> cookie_changes_1, cookie_changes_2;
std::unique_ptr<CookieChangeSubscription> subscription1 =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->http_www_foo_.url(), "abc",
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
base::Unretained(&cookie_changes_1)));
std::unique_ptr<CookieChangeSubscription> subscription2 =
cs->GetChangeDispatcher().AddCallbackForCookie(
this->http_bar_com_.url(), "ghi",
base::BindRepeating(
&CookieStoreChangeTestBase<TypeParam>::