blob: 699bd7b7319108eb693a77e4eb19725d03e0f31f [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ANDROID_WEBVIEW_BROWSER_PREFETCH_AW_PREFETCH_MANAGER_H_
#define ANDROID_WEBVIEW_BROWSER_PREFETCH_AW_PREFETCH_MANAGER_H_
#include "base/containers/circular_deque.h"
#include "base/memory/raw_ref.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/prefetch_handle.h"
#include "content/public/browser/prefetch_request_status_listener.h"
#include "net/http/http_no_vary_search_data.h"
#include "net/http/http_request_headers.h"
#include "services/network/public/cpp/resource_request.h"
#include "url/gurl.h"
namespace android_webview {
// The default TTL value in `//content` is 10 minutes which is too long for most
// of WebView cases. This value here can change in the future and that shouldn't
// affect the `//content` TTL default value.
inline constexpr int DEFAULT_TTL_IN_SEC = 60;
// The MaxPrefetches number is not present in the `//content` layer, so it is
// specific to WebView.
inline constexpr size_t DEFAULT_MAX_PREFETCHES = 10;
// This is the source of truth for the absolute maximum number of prefetches
// that can ever be cached in WebView. It can override the number set by the
// AndroidX API.
inline constexpr int32_t ABSOLUTE_MAX_PREFETCHES = 20;
// Returned from `AwPrefetchManager::StartPrefetchRequest` if the prefetch
// request was unsuccessful (i.e. there is no key for the prefetch).
inline constexpr int NO_PREFETCH_KEY = -1;
// The suffix used for generating `//content` prefetch internal histogram names
// recorded per trigger.
// TODO(crbug.com/379140429): Merge this with prerender one.
inline constexpr char AW_PREFETCH_METRICS_SUFFIX[] = "WebView";
// Manages prefetch operations for this Profile.
// Lifetime: Profile
class AwPrefetchManager {
public:
explicit AwPrefetchManager(content::BrowserContext* browser_context);
AwPrefetchManager(const AwPrefetchManager&) = delete;
AwPrefetchManager& operator=(const AwPrefetchManager&) = delete;
~AwPrefetchManager();
// Returns `true` if the `resource_request` is also a prefetch request.
// NOTE: A prefetch request can also be a prerender request i.e.
// this method & `IsPrerenderRequest` can both return `true` for it,
// however this is not always the case.
static bool IsPrefetchRequest(
const network::ResourceRequest& resource_request);
// Returns `true` if the `resource_request` is also a prerender request.
// NOTE: A prerender request will always be a prefetch request i.e.
// this method & `IsPrefetchRequest` will always return `true` for it
// as prefetching a always required for prerendering.
static bool IsPrerenderRequest(
const network::ResourceRequest& resource_request);
// Returns `true` if the `blink::kSecPurposeHeaderName` header is associated
// with a prefetch request.
static bool IsSecPurposeForPrefetch(
std::optional<std::string> sec_purpose_header_value);
// Returns the key associated with the outgoing prefetch request
// and thus the prefetch handle inside of `all_prefetches_map_` (if
// successful), otherwise returns `NO_PREFETCH_KEY`.
int StartPrefetchRequest(
JNIEnv* env,
const std::string& url,
const base::android::JavaParamRef<jobject>& prefetch_params,
const base::android::JavaParamRef<jobject>& callback,
const base::android::JavaParamRef<jobject>& callback_executor);
void CancelPrefetch(JNIEnv* env, jint prefetch_key);
bool GetIsPrefetchInCacheForTesting(JNIEnv* env, jint prefetch_key);
// Updates Time-To-Live (TTL) for the prefetched content in seconds.
void SetTtlInSec(JNIEnv* env, jint ttl_in_sec) { ttl_in_sec_ = ttl_in_sec; }
// Updates the maximum number of allowed prefetches in cache
void SetMaxPrefetches(JNIEnv* env, jint max_prefetches) {
max_prefetches_ = std::min(max_prefetches, ABSOLUTE_MAX_PREFETCHES);
}
// Returns the Time-to-Live (TTL) for prefetched content in seconds.
jint GetTtlInSec(JNIEnv* env) const { return ttl_in_sec_; }
// Returns the maximum number of allowed prefetches in cache.
jint GetMaxPrefetches(JNIEnv* env) const { return max_prefetches_; }
// Returns the key associated with the prefetch handle inside of
// `all_prefetches_map_`.
int AddPrefetchHandle(
std::unique_ptr<content::PrefetchHandle> prefetch_handle) {
CHECK(prefetch_handle);
CHECK(max_prefetches_ > 0u);
CHECK(all_prefetches_map_.size() < max_prefetches_);
const int32_t new_prefetch_key = GetNextPrefetchKey();
all_prefetches_map_[new_prefetch_key] = std::move(prefetch_handle);
UpdateLastPrefetchKey(new_prefetch_key);
return new_prefetch_key;
}
std::vector<int32_t> GetAllPrefetchKeysForTesting() const {
std::vector<int32_t> prefetch_keys;
prefetch_keys.reserve(all_prefetches_map_.size());
for (const auto& prefetch_pair : all_prefetches_map_) {
prefetch_keys.push_back(prefetch_pair.first);
}
return prefetch_keys;
}
int GetLastPrefetchKeyForTesting() const { return last_prefetch_key_; }
base::android::ScopedJavaLocalRef<jobject> GetJavaPrefetchManager();
private:
raw_ref<content::BrowserContext> browser_context_;
int ttl_in_sec_ = DEFAULT_TTL_IN_SEC;
size_t max_prefetches_ = DEFAULT_MAX_PREFETCHES;
std::map<int32_t, std::unique_ptr<content::PrefetchHandle>>
all_prefetches_map_;
// Java object reference.
base::android::ScopedJavaGlobalRef<jobject> java_obj_;
// Should only be incremented. Acts as an "order added" mechanism
// inside of `all_prefetches_map_` since `std::map` stores keys
// in a sorted order.
int32_t last_prefetch_key_ = -1;
int32_t GetNextPrefetchKey() const { return last_prefetch_key_ + 1; }
void UpdateLastPrefetchKey(int new_key) {
CHECK(new_key > last_prefetch_key_);
last_prefetch_key_ = new_key;
}
};
} // namespace android_webview
#endif // ANDROID_WEBVIEW_BROWSER_PREFETCH_AW_PREFETCH_MANAGER_H_