blob: 8af8fa3402a90aefc8d4b162bebc391cc665520d [file]
// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "android_webview/browser/prefetch/aw_prefetch_manager_data.h"
#include "android_webview/browser/prefetch/aw_preloading_utils.h"
#include "android_webview/common/aw_features.h"
#include "content/public/browser/browser_thread.h"
namespace android_webview {
using content::BrowserThread;
AwPrefetchManagerData::AwPrefetchManagerData()
: lock_(IsWebViewPrefetchOffTheMainThreadEnabled()
? std::make_unique<base::Lock>()
: nullptr) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
}
AwPrefetchManagerData::~AwPrefetchManagerData() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
}
AwPrefetchKey AwPrefetchManagerData::AddNewPrefetchHandleWrapper(
std::unique_ptr<AwPrefetchHandleWrapper> prefetch_handle_wrapper) {
CHECK(!IsWebViewPrefetchOffTheMainThreadEnabled());
int32_t new_prefetch_key;
base::AutoLockMaybe auto_lock(lock_.get());
CHECK(prefetch_handle_wrapper);
CHECK(max_prefetches_ > 0u);
CHECK(all_prefetches_map_.size() < max_prefetches_);
new_prefetch_key = GetNextPrefetchKeyLocked();
// `all_prefetches_map_[new_prefetch_key]` should have no entry because
// `GetNextPrefetchKeyLocked()` always returns a new key.
all_prefetches_map_[new_prefetch_key] = std::move(prefetch_handle_wrapper);
UpdateLastPrefetchKeyLocked(new_prefetch_key);
return new_prefetch_key;
}
AwPrefetchKey AwPrefetchManagerData::ReservePrefetchHandleWrapper(
const GURL& url,
const std::optional<net::HttpNoVarySearchData>& expected_no_vary_search) {
CHECK(IsWebViewPrefetchOffTheMainThreadEnabled());
std::vector<std::unique_ptr<AwPrefetchHandleWrapper>>
old_prefetch_handle_wrappers;
AwPrefetchKey new_prefetch_key;
{
base::AutoLockMaybe auto_lock(lock_.get());
bool is_duplicate = IsPrefetchDuplicateLocked(url, expected_no_vary_search);
if (is_duplicate) {
return NO_PREFETCH_KEY;
}
// Evict oldest handles if necessary.
old_prefetch_handle_wrappers =
MayEvictOldestPrefetchHandleForANewRequestLocked();
new_prefetch_key = GetNextPrefetchKeyLocked();
// `all_prefetches_map_[new_prefetch_key]` should have no entry because
// `GetNextPrefetchKeyLocked()` always returns a new key.
CHECK(!all_prefetches_map_[new_prefetch_key]);
all_prefetches_map_[new_prefetch_key] =
std::make_unique<AwPrefetchHandleWrapper>(url, expected_no_vary_search);
UpdateLastPrefetchKeyLocked(new_prefetch_key);
}
return new_prefetch_key;
// `old_prefetch_handle_wrappers` is dropped here, outside of the
// `lock_` to prevent accidental reentrancy.
}
void AwPrefetchManagerData::CommitInitialPrePrefetchHandle(
AwPrefetchKey prefetch_key,
std::unique_ptr<content::PrePrefetchHandle> pre_prefetch_handle) {
CHECK(IsWebViewPrefetchOffTheMainThreadEnabled());
base::AutoLockMaybe auto_lock(lock_.get());
auto it = all_prefetches_map_.find(prefetch_key);
if (it != all_prefetches_map_.end()) {
it->second->CommitInitialPrePrefetchHandle(std::move(pre_prefetch_handle));
}
}
void AwPrefetchManagerData::CommitInitialPrefetchHandle(
AwPrefetchKey prefetch_key,
std::unique_ptr<content::PrefetchHandle> prefetch_handle) {
CHECK(IsWebViewPrefetchOffTheMainThreadEnabled());
base::AutoLockMaybe auto_lock(lock_.get());
auto it = all_prefetches_map_.find(prefetch_key);
if (it != all_prefetches_map_.end()) {
it->second->CommitInitialPrefetchHandle(std::move(prefetch_handle));
}
}
std::unique_ptr<content::PrePrefetchHandle>
AwPrefetchManagerData::TakePrePrefetchHandleForConsume(
AwPrefetchKey prefetch_key) {
base::AutoLockMaybe auto_lock(lock_.get());
auto it = all_prefetches_map_.find(prefetch_key);
if (it != all_prefetches_map_.end() &&
it->second->CanTakePrePrefetchHandleForConsume()) {
return it->second->TakePrePrefetchHandleForConsume();
}
return nullptr;
}
void AwPrefetchManagerData::CommitPrefetchHandleAfterConsume(
AwPrefetchKey prefetch_key,
std::unique_ptr<content::PrefetchHandle> prefetch_handle) {
base::AutoLockMaybe auto_lock(lock_.get());
auto it = all_prefetches_map_.find(prefetch_key);
if (it != all_prefetches_map_.end()) {
it->second->CommitPrefetchHandleAfterConsume(std::move(prefetch_handle));
}
// If the prefetch was already removed by other calls, this does nothing.
// `prefetch_handle` will be released here.
}
bool AwPrefetchManagerData::IsPrefetchDuplicateLocked(
const GURL& url,
const std::optional<net::HttpNoVarySearchData>& expected_no_vary_search)
const {
std::vector<const content::PrefetchDeduplicationEntry*> candidates;
candidates.reserve(all_prefetches_map_.size());
for (const auto& [_, prefetch_handle_wrapper] : all_prefetches_map_) {
candidates.push_back(prefetch_handle_wrapper.get());
}
return content::IsPrefetchDuplicate(candidates, url, expected_no_vary_search);
}
std::vector<std::unique_ptr<AwPrefetchHandleWrapper>>
AwPrefetchManagerData::MayEvictOldestPrefetchHandleForANewRequestLocked() {
std::vector<std::unique_ptr<AwPrefetchHandleWrapper>>
old_prefetch_handle_wrappers;
if (all_prefetches_map_.size() >= max_prefetches_) {
int num_prefetches_to_evict =
all_prefetches_map_.size() - max_prefetches_ + 1;
auto it = all_prefetches_map_.begin();
while (num_prefetches_to_evict > 0 && it != all_prefetches_map_.end()) {
// Because the keys should be sequential based on when the prefetch
// associated with them was added, a standard iteration should always
// prioritize removing the oldest entry.
old_prefetch_handle_wrappers.push_back(std::move(it->second));
it = all_prefetches_map_.erase(it);
num_prefetches_to_evict--;
}
}
return old_prefetch_handle_wrappers;
}
void AwPrefetchManagerData::MayEvictOldestPrefetchHandleForANewRequest() {
std::vector<std::unique_ptr<AwPrefetchHandleWrapper>>
old_prefetch_handle_wrappers;
{
base::AutoLockMaybe auto_lock(lock_.get());
old_prefetch_handle_wrappers =
MayEvictOldestPrefetchHandleForANewRequestLocked();
}
// `old_prefetch_handle_wrappers` is dropped here, outside of the
// `lock_` to prevent accidental reentrancy.
}
void AwPrefetchManagerData::CancelPrefetch(AwPrefetchKey prefetch_key) {
std::unique_ptr<AwPrefetchHandleWrapper> old_prefetch_handle_wrapper;
{
base::AutoLockMaybe auto_lock(lock_.get());
auto it = all_prefetches_map_.find(prefetch_key);
if (it != all_prefetches_map_.end()) {
old_prefetch_handle_wrapper = std::move(it->second);
all_prefetches_map_.erase(it);
}
}
// `old_prefetch_handle_wrapper` is dropped here, outside of the
// `lock_` to prevent accidental reentrancy.
}
bool AwPrefetchManagerData::UpdateLatestPrefetchInfo(
const AwPrefetchLatestInfoPref& info) {
base::AutoLockMaybe auto_lock(lock_.get());
CHECK(IsWebViewPrefetchOffTheMainThreadEnabled());
if (prefetch_latest_info_ == info) {
return false;
}
prefetch_latest_info_ = info;
return true;
}
void AwPrefetchManagerData::SetTtlInSec(int ttl_in_sec) {
base::AutoLockMaybe auto_lock(lock_.get());
ttl_in_sec_ = ttl_in_sec;
}
void AwPrefetchManagerData::SetMaxPrefetches(size_t max_prefetches) {
base::AutoLockMaybe auto_lock(lock_.get());
max_prefetches_ = max_prefetches;
}
int AwPrefetchManagerData::GetTtlInSec() const {
base::AutoLockMaybe auto_lock(lock_.get());
return ttl_in_sec_;
}
size_t AwPrefetchManagerData::GetMaxPrefetches() const {
base::AutoLockMaybe auto_lock(lock_.get());
return max_prefetches_;
}
std::vector<AwPrefetchKey>
AwPrefetchManagerData::GetAllPrefetchKeysForTesting() // IN-TEST
const {
base::AutoLockMaybe auto_lock(lock_.get());
std::vector<AwPrefetchKey> prefetch_keys;
prefetch_keys.reserve(all_prefetches_map_.size());
for (const auto& [key, prefetch_handle_wrapper] : all_prefetches_map_) {
prefetch_keys.push_back(key);
}
return prefetch_keys;
}
AwPrefetchKey AwPrefetchManagerData::GetLastPrefetchKeyForTesting() // IN-TEST
const {
base::AutoLockMaybe auto_lock(lock_.get());
return last_prefetch_key_;
}
bool AwPrefetchManagerData::GetIsPrefetchInCacheForTesting( // IN-TEST
AwPrefetchKey prefetch_key) const {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
base::AutoLockMaybe auto_lock(lock_.get());
return all_prefetches_map_.find(prefetch_key) != all_prefetches_map_.end();
}
AwPrefetchKey AwPrefetchManagerData::GetNextPrefetchKeyLocked() const {
return last_prefetch_key_ + 1;
}
void AwPrefetchManagerData::UpdateLastPrefetchKeyLocked(AwPrefetchKey new_key) {
CHECK(new_key > last_prefetch_key_);
last_prefetch_key_ = new_key;
}
} // namespace android_webview