286 lines
9.8 KiB
C++
286 lines
9.8 KiB
C++
// Copyright 2018 The Chromium Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "net/cookies/cookie_monster_change_dispatcher.h"
|
|
|
|
#include <utility>
|
|
|
|
#include "base/functional/bind.h"
|
|
#include "base/strings/string_piece.h"
|
|
#include "base/task/single_thread_task_runner.h"
|
|
#include "base/task/task_runner.h"
|
|
#include "net/base/features.h"
|
|
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
|
|
#include "net/cookies/canonical_cookie.h"
|
|
#include "net/cookies/cookie_access_delegate.h"
|
|
#include "net/cookies/cookie_change_dispatcher.h"
|
|
#include "net/cookies/cookie_constants.h"
|
|
#include "net/cookies/cookie_monster.h"
|
|
#include "net/cookies/cookie_util.h"
|
|
|
|
namespace net {
|
|
|
|
namespace {
|
|
|
|
// Special key in GlobalDomainMap for global listeners.
|
|
constexpr base::StringPiece kGlobalDomainKey = base::StringPiece("\0", 1);
|
|
|
|
//
|
|
constexpr base::StringPiece kGlobalNameKey = base::StringPiece("\0", 1);
|
|
|
|
} // anonymous namespace
|
|
|
|
CookieMonsterChangeDispatcher::Subscription::Subscription(
|
|
base::WeakPtr<CookieMonsterChangeDispatcher> change_dispatcher,
|
|
std::string domain_key,
|
|
std::string name_key,
|
|
GURL url,
|
|
CookiePartitionKeyCollection cookie_partition_key_collection,
|
|
bool same_party_attribute_enabled,
|
|
net::CookieChangeCallback callback)
|
|
: change_dispatcher_(std::move(change_dispatcher)),
|
|
domain_key_(std::move(domain_key)),
|
|
name_key_(std::move(name_key)),
|
|
url_(std::move(url)),
|
|
cookie_partition_key_collection_(
|
|
std::move(cookie_partition_key_collection)),
|
|
callback_(std::move(callback)),
|
|
same_party_attribute_enabled_(same_party_attribute_enabled),
|
|
task_runner_(base::SingleThreadTaskRunner::GetCurrentDefault()) {
|
|
DCHECK(url_.is_valid() || url_.is_empty());
|
|
DCHECK_EQ(url_.is_empty(), domain_key_ == kGlobalDomainKey);
|
|
}
|
|
|
|
CookieMonsterChangeDispatcher::Subscription::~Subscription() {
|
|
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
|
|
|
|
if (change_dispatcher_) {
|
|
change_dispatcher_->UnlinkSubscription(this);
|
|
}
|
|
}
|
|
|
|
void CookieMonsterChangeDispatcher::Subscription::DispatchChange(
|
|
const CookieChangeInfo& change,
|
|
const CookieAccessDelegate* cookie_access_delegate) {
|
|
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
|
|
|
|
const CanonicalCookie& cookie = change.cookie;
|
|
|
|
// The net::CookieOptions are hard-coded for now, but future APIs may set
|
|
// different options. For example, JavaScript observers will not be allowed to
|
|
// see HTTP-only changes.
|
|
if (!url_.is_empty()) {
|
|
bool delegate_treats_url_as_trustworthy =
|
|
cookie_access_delegate &&
|
|
cookie_access_delegate->ShouldTreatUrlAsTrustworthy(url_);
|
|
CookieOptions options = CookieOptions::MakeAllInclusive();
|
|
CookieSamePartyStatus same_party_status = cookie_util::GetSamePartyStatus(
|
|
cookie, options, same_party_attribute_enabled_);
|
|
if (!cookie
|
|
.IncludeForRequestURL(
|
|
url_, options,
|
|
CookieAccessParams{change.access_result.access_semantics,
|
|
delegate_treats_url_as_trustworthy,
|
|
same_party_status})
|
|
.status.IsInclude()) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!cookie_partition_key_collection_.ContainsAllKeys()) {
|
|
if (cookie_partition_key_collection_.PartitionKeys().empty()) {
|
|
if (cookie.IsPartitioned()) {
|
|
return;
|
|
}
|
|
} else {
|
|
DCHECK_EQ(1u, cookie_partition_key_collection_.PartitionKeys().size());
|
|
const CookiePartitionKey& key =
|
|
*cookie_partition_key_collection_.PartitionKeys().begin();
|
|
if (CookiePartitionKey::HasNonce(key) && !cookie.IsPartitioned()) {
|
|
return;
|
|
}
|
|
if (cookie.IsPartitioned() && key != *cookie.PartitionKey()) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO(mmenke, pwnall): Run callbacks synchronously?
|
|
task_runner_->PostTask(
|
|
FROM_HERE, base::BindOnce(&Subscription::DoDispatchChange,
|
|
weak_ptr_factory_.GetWeakPtr(), change));
|
|
}
|
|
|
|
void CookieMonsterChangeDispatcher::Subscription::DoDispatchChange(
|
|
const CookieChangeInfo& change) const {
|
|
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
|
|
|
|
callback_.Run(change);
|
|
}
|
|
|
|
CookieMonsterChangeDispatcher::CookieMonsterChangeDispatcher(
|
|
const CookieMonster* cookie_monster,
|
|
bool same_party_attribute_enabled)
|
|
: cookie_monster_(cookie_monster),
|
|
same_party_attribute_enabled_(same_party_attribute_enabled) {}
|
|
|
|
CookieMonsterChangeDispatcher::~CookieMonsterChangeDispatcher() {
|
|
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
|
|
}
|
|
|
|
// static
|
|
std::string CookieMonsterChangeDispatcher::DomainKey(
|
|
const std::string& domain) {
|
|
std::string domain_key =
|
|
net::registry_controlled_domains::GetDomainAndRegistry(
|
|
domain, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
|
|
DCHECK_NE(domain_key, kGlobalDomainKey);
|
|
return domain_key;
|
|
}
|
|
|
|
// static
|
|
std::string CookieMonsterChangeDispatcher::DomainKey(const GURL& url) {
|
|
std::string domain_key =
|
|
net::registry_controlled_domains::GetDomainAndRegistry(
|
|
url, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
|
|
DCHECK_NE(domain_key, kGlobalDomainKey);
|
|
return domain_key;
|
|
}
|
|
|
|
// static
|
|
std::string CookieMonsterChangeDispatcher::NameKey(std::string name) {
|
|
DCHECK_NE(name, kGlobalNameKey);
|
|
return name;
|
|
}
|
|
|
|
std::unique_ptr<CookieChangeSubscription>
|
|
CookieMonsterChangeDispatcher::AddCallbackForCookie(
|
|
const GURL& url,
|
|
const std::string& name,
|
|
const absl::optional<CookiePartitionKey>& cookie_partition_key,
|
|
CookieChangeCallback callback) {
|
|
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
|
|
|
|
std::unique_ptr<Subscription> subscription = std::make_unique<Subscription>(
|
|
weak_ptr_factory_.GetWeakPtr(), DomainKey(url), NameKey(name), url,
|
|
CookiePartitionKeyCollection::FromOptional(cookie_partition_key),
|
|
same_party_attribute_enabled_, std::move(callback));
|
|
|
|
LinkSubscription(subscription.get());
|
|
return subscription;
|
|
}
|
|
|
|
std::unique_ptr<CookieChangeSubscription>
|
|
CookieMonsterChangeDispatcher::AddCallbackForUrl(
|
|
const GURL& url,
|
|
const absl::optional<CookiePartitionKey>& cookie_partition_key,
|
|
CookieChangeCallback callback) {
|
|
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
|
|
|
|
std::unique_ptr<Subscription> subscription = std::make_unique<Subscription>(
|
|
weak_ptr_factory_.GetWeakPtr(), DomainKey(url),
|
|
std::string(kGlobalNameKey), url,
|
|
CookiePartitionKeyCollection::FromOptional(cookie_partition_key),
|
|
same_party_attribute_enabled_, std::move(callback));
|
|
|
|
LinkSubscription(subscription.get());
|
|
return subscription;
|
|
}
|
|
|
|
std::unique_ptr<CookieChangeSubscription>
|
|
CookieMonsterChangeDispatcher::AddCallbackForAllChanges(
|
|
CookieChangeCallback callback) {
|
|
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
|
|
|
|
std::unique_ptr<Subscription> subscription = std::make_unique<Subscription>(
|
|
weak_ptr_factory_.GetWeakPtr(), std::string(kGlobalDomainKey),
|
|
std::string(kGlobalNameKey), GURL(""),
|
|
CookiePartitionKeyCollection::ContainsAll(),
|
|
same_party_attribute_enabled_, std::move(callback));
|
|
|
|
LinkSubscription(subscription.get());
|
|
return subscription;
|
|
}
|
|
|
|
void CookieMonsterChangeDispatcher::DispatchChange(
|
|
const CookieChangeInfo& change,
|
|
bool notify_global_hooks) {
|
|
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
|
|
|
|
DispatchChangeToDomainKey(change, DomainKey(change.cookie.Domain()));
|
|
if (notify_global_hooks)
|
|
DispatchChangeToDomainKey(change, std::string(kGlobalDomainKey));
|
|
}
|
|
|
|
void CookieMonsterChangeDispatcher::DispatchChangeToDomainKey(
|
|
const CookieChangeInfo& change,
|
|
const std::string& domain_key) {
|
|
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
|
|
|
|
auto it = cookie_domain_map_.find(domain_key);
|
|
if (it == cookie_domain_map_.end())
|
|
return;
|
|
|
|
DispatchChangeToNameKey(change, it->second, NameKey(change.cookie.Name()));
|
|
DispatchChangeToNameKey(change, it->second, std::string(kGlobalNameKey));
|
|
}
|
|
|
|
void CookieMonsterChangeDispatcher::DispatchChangeToNameKey(
|
|
const CookieChangeInfo& change,
|
|
CookieNameMap& cookie_name_map,
|
|
const std::string& name_key) {
|
|
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
|
|
|
|
auto it = cookie_name_map.find(name_key);
|
|
if (it == cookie_name_map.end())
|
|
return;
|
|
|
|
SubscriptionList& subscription_list = it->second;
|
|
for (base::LinkNode<Subscription>* node = subscription_list.head();
|
|
node != subscription_list.end(); node = node->next()) {
|
|
node->value()->DispatchChange(change,
|
|
cookie_monster_->cookie_access_delegate());
|
|
}
|
|
}
|
|
|
|
void CookieMonsterChangeDispatcher::LinkSubscription(
|
|
Subscription* subscription) {
|
|
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
|
|
|
|
// The subscript operator creates empty maps if the lookups fail. This is
|
|
// exactly what this method needs.
|
|
CookieNameMap& cookie_name_map =
|
|
cookie_domain_map_[subscription->domain_key()];
|
|
SubscriptionList& subscription_list =
|
|
cookie_name_map[subscription->name_key()];
|
|
subscription_list.Append(subscription);
|
|
}
|
|
|
|
void CookieMonsterChangeDispatcher::UnlinkSubscription(
|
|
Subscription* subscription) {
|
|
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
|
|
|
|
auto cookie_domain_map_iterator =
|
|
cookie_domain_map_.find(subscription->domain_key());
|
|
DCHECK(cookie_domain_map_iterator != cookie_domain_map_.end());
|
|
|
|
CookieNameMap& cookie_name_map = cookie_domain_map_iterator->second;
|
|
auto cookie_name_map_iterator =
|
|
cookie_name_map.find(subscription->name_key());
|
|
DCHECK(cookie_name_map_iterator != cookie_name_map.end());
|
|
|
|
SubscriptionList& subscription_list = cookie_name_map_iterator->second;
|
|
subscription->RemoveFromList();
|
|
if (!subscription_list.empty())
|
|
return;
|
|
|
|
cookie_name_map.erase(cookie_name_map_iterator);
|
|
if (!cookie_name_map.empty())
|
|
return;
|
|
|
|
cookie_domain_map_.erase(cookie_domain_map_iterator);
|
|
}
|
|
|
|
} // namespace net
|