// 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_CASE_P(CookieStoreChangeGlobalTest);

template <class CookieStoreTestTraits>
class CookieStoreChangeUrlTest
    : public CookieStoreChangeTestBase<CookieStoreTestTraits> {};
TYPED_TEST_CASE_P(CookieStoreChangeUrlTest);

template <class CookieStoreTestTraits>
class CookieStoreChangeNamedTest
    : public CookieStoreChangeTestBase<CookieStoreTestTraits> {};
TYPED_TEST_CASE_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>::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].first.Name());
  EXPECT_EQ("def", 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("ghi", cookie_changes_2[0].first.Name());
  EXPECT_EQ("jkl", cookie_changes_2[0].first.Value());
  EXPECT_EQ(this->http_bar_com_.url().host(),
            cookie_changes_2[0].first.Domain());
}

TYPED_TEST_P(CookieStoreChangeNamedTest, DifferentSubscriptionsDomains) {
  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(), "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());

  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].first.Name());
  EXPECT_EQ("def", 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("abc", cookie_changes_2[0].first.Name());
  EXPECT_EQ("ghi", cookie_changes_2[0].first.Value());
  EXPECT_EQ(this->http_bar_com_.url().host(),
            cookie_changes_2[0].first.Domain());
}

TYPED_TEST_P(CookieStoreChangeNamedTest, DifferentSubscriptionsNames) {
  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_www_foo_.url(), "ghi",
          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(), "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].first.Name());
  EXPECT_EQ("def", 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("ghi", cookie_changes_2[0].first.Name());
  EXPECT_EQ("jkl", cookie_changes_2[0].first.Value());
  EXPECT_EQ(this->http_www_foo_.url().host(),
            cookie_changes_2[0].first.Domain());
}

TYPED_TEST_P(CookieStoreChangeNamedTest, DifferentSubscriptionsPaths) {
  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->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());

  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].first.Name());
  EXPECT_EQ("def", 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("abc", cookie_changes_2[0].first.Name());
  EXPECT_EQ("def", 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("abc", cookie_changes_2[1].first.Name());
  EXPECT_EQ("ghi", 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(CookieStoreChangeNamedTest, DifferentSubscriptionsFiltering) {
  if (!TypeParam::supports_named_cookie_tracking)
    return;

  CookieStore* cs = this->GetCookieStore();

  std::vector<CookieChange> cookie_changes_1, cookie_changes_2;
  std::vector<CookieChange> cookie_changes_3, cookie_changes_4;
  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_www_foo_.url(), "hij",
          base::BindRepeating(
              &CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
              base::Unretained(&cookie_changes_2)));
  std::unique_ptr<CookieChangeSubscription> subscription3 =
      cs->GetChangeDispatcher().AddCallbackForCookie(
          this->http_bar_com_.url(), "abc",
          base::BindRepeating(
              &CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
              base::Unretained(&cookie_changes_3)));
  std::unique_ptr<CookieChangeSubscription> subscription4 =
      cs->GetChangeDispatcher().AddCallbackForCookie(
          this->www_foo_foo_.url(), "abc",
          base::BindRepeating(
              &CookieStoreChangeTestBase<TypeParam>::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].first.Name());
  EXPECT_EQ("def", 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("hij", cookie_changes_2[0].first.Name());
  EXPECT_EQ("mno", cookie_changes_2[0].first.Value());
  EXPECT_EQ(this->http_www_foo_.url().host(),
            cookie_changes_2[0].first.Domain());
  EXPECT_EQ(1u, cookie_changes_2.size());

  ASSERT_LE(1u, cookie_changes_3.size());
  EXPECT_EQ("abc", cookie_changes_3[0].first.Name());
  EXPECT_EQ("stu", cookie_changes_3[0].first.Value());
  EXPECT_EQ(this->http_bar_com_.url().host(),
            cookie_changes_3[0].first.Domain());
  EXPECT_EQ(1u, cookie_changes_3.size());

  ASSERT_LE(1u, cookie_changes_4.size());
  EXPECT_EQ("abc", cookie_changes_4[0].first.Name());
  EXPECT_EQ("def", cookie_changes_4[0].first.Value());
  EXPECT_EQ("/", cookie_changes_4[0].first.Path());
  EXPECT_EQ(this->http_www_foo_.url().host(),
            cookie_changes_4[0].first.Domain());

  ASSERT_LE(2u, cookie_changes_4.size());
  EXPECT_EQ("abc", cookie_changes_4[1].first.Name());
  EXPECT_EQ("vwx", cookie_changes_4[1].first.Value());
  EXPECT_EQ("/foo", cookie_changes_4[1].first.Path());
  EXPECT_EQ(this->http_www_foo_.url().host(),
            cookie_changes_4[1].first.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<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_www_foo_.url(), "abc",
          base::BindRepeating(
              &CookieStoreChangeTestBase<TypeParam>::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].first.Name());
  EXPECT_EQ("def", cookie_changes_1[0].first.Value());
  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());
  cookie_changes_2.clear();
}

TYPED_TEST_P(CookieStoreChangeNamedTest, SubscriptionOutlivesStore) {
  if (!TypeParam::supports_named_cookie_tracking)
    return;

  std::vector<CookieChange> cookie_changes;
  std::unique_ptr<CookieChangeSubscription> subscription =
      this->GetCookieStore()->GetChangeDispatcher().AddCallbackForCookie(
          this->http_www_foo_.url(), "abc",
          base::BindRepeating(
              &CookieStoreChangeTestBase<TypeParam>::OnCookieChange,
              base::Unretained(&cookie_changes)));
  this->ResetCookieStore();

  // |subscription| outlives cookie_store - crash should not happen.
  subscription.reset();
}

REGISTER_TYPED_TEST_CASE_P(CookieStoreChangeGlobalTest,
                           NoCookie,
                           InitialCookie,
                           InsertOne,
                           InsertMany,
                           DeleteOne,
                           DeleteTwo,
                           Overwrite,
                           OverwriteWithHttpOnly,
                           Deregister,
                           DeregisterMultiple,
                           DispatchRace,
                           DeregisterRace,
                           DeregisterRaceMultiple,
                           MultipleSubscriptions);

REGISTER_TYPED_TEST_CASE_P(CookieStoreChangeUrlTest,
                           NoCookie,
                           InitialCookie,
                           InsertOne,
                           InsertMany,
                           InsertFiltering,
                           DeleteOne,
                           DeleteTwo,
                           DeleteFiltering,
                           Overwrite,
                           OverwriteFiltering,
                           OverwriteWithHttpOnly,
                           Deregister,
                           DeregisterMultiple,
                           DispatchRace,
                           DeregisterRace,
                           DeregisterRaceMultiple,
                           DifferentSubscriptionsDisjoint,
                           DifferentSubscriptionsDomains,
                           DifferentSubscriptionsPaths,
                           DifferentSubscriptionsFiltering,
                           MultipleSubscriptions);

REGISTER_TYPED_TEST_CASE_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);

}  // namespace net

#endif  // NET_COOKIES_COOKIE_STORE_CHANGE_UNITTEST_H
