1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
content / browser / interest_group / interest_group_permissions_cache.cc [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/interest_group/interest_group_permissions_cache.h"
#include <map>
#include <memory>
#include "base/containers/lru_cache.h"
#include "base/functional/callback.h"
#include "base/location.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "net/base/network_isolation_key.h"
#include "url/origin.h"
namespace content {
InterestGroupPermissionsCache::InterestGroupPermissionsCache() = default;
InterestGroupPermissionsCache::~InterestGroupPermissionsCache() = default;
InterestGroupPermissionsCache::Permissions*
InterestGroupPermissionsCache::GetPermissions(
const url::Origin& frame_origin,
const url::Origin& interest_group_owner,
const net::NetworkIsolationKey& network_isolation_key) {
base::TimeTicks now = base::TimeTicks::Now();
CacheShard* shard = FindShard(frame_origin, network_isolation_key, now);
if (!shard)
return nullptr;
auto cache_entry = shard->cache->Get(interest_group_owner);
if (cache_entry == shard->cache->end())
return nullptr;
if (cache_entry->second.expiry < now) {
// Delete the LRU cache entry if it has expired.
shard->cache->Erase(cache_entry);
return nullptr;
}
return &cache_entry->second.permissions;
}
void InterestGroupPermissionsCache::CachePermissions(
Permissions permissions,
const url::Origin& frame_origin,
const url::Origin& interest_group_owner,
const net::NetworkIsolationKey& network_isolation_key) {
base::TimeTicks now = base::TimeTicks::Now();
// Use FindShard() here to remove shard if it has expired. Reusing expired
// entries that haven't been cleaned up yet would potentially leak details
// about when the cleanup expired task has last run, which can be influenced
// by calls made from cross-origin renderers.
CacheShard* shard = FindShard(frame_origin, network_isolation_key, now);
if (!shard) {
shard = &cache_shards_
.emplace(std::make_pair(
CacheShardKey{frame_origin, network_isolation_key},
CacheShard()))
.first->second;
}
base::TimeTicks expiry = now + kCacheDuration;
shard->cache->Put({interest_group_owner, CacheEntry{expiry, permissions}});
// Update the shard expiry to match that of the newly added CacheEntry, as
// that should be longest lived entry.
shard->expiry = expiry;
MaybeStartDeleteExpiredTimer();
}
void InterestGroupPermissionsCache::Clear() {
cache_shards_.clear();
}
size_t InterestGroupPermissionsCache::cache_shards_for_testing() const {
return cache_shards_.size();
}
InterestGroupPermissionsCache::CacheShard::CacheShard()
: cache(std::make_unique<base::LRUCache<url::Origin, CacheEntry>>(
kMaxCacheEntriesPerShard)) {}
InterestGroupPermissionsCache::CacheShard::CacheShard(CacheShard&&) = default;
InterestGroupPermissionsCache::CacheShard::~CacheShard() = default;
InterestGroupPermissionsCache::CacheShard*
InterestGroupPermissionsCache::FindShard(
const url::Origin& frame_origin,
const net::NetworkIsolationKey& network_isolation_key,
base::TimeTicks now) {
auto shard =
cache_shards_.find(CacheShardKey{frame_origin, network_isolation_key});
if (shard == cache_shards_.end())
return nullptr;
if (shard->second.expiry < now) {
cache_shards_.erase(shard);
return nullptr;
}
return &shard->second;
}
void InterestGroupPermissionsCache::MaybeStartDeleteExpiredTimer() {
if (cache_shards_.empty() || delete_expired_timer_.IsRunning())
return;
delete_expired_timer_.Start(
FROM_HERE, kDeleteExpiredTimerDuration,
base::BindOnce(&InterestGroupPermissionsCache::DeleteExpired,
base::Unretained(this)));
}
void InterestGroupPermissionsCache::DeleteExpired() {
base::TimeTicks now = base::TimeTicks::Now();
for (auto shard = cache_shards_.begin(); shard != cache_shards_.end();) {
auto current_shard = shard;
shard = ++shard;
if (current_shard->second.expiry < now)
cache_shards_.erase(current_shard);
}
MaybeStartDeleteExpiredTimer();
}
} // namespace content