910e62b5创建于 1月15日历史提交
// 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_