blob: e65e1008792a10a6554dabe35c31cb9fd94ee130 [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_handle_wrapper.h"
#include <ostream>
#include "android_webview/browser/prefetch/aw_preloading_utils.h"
#include "android_webview/common/aw_features.h"
#include "base/state_transitions.h"
#include "content/public/browser/browser_thread.h"
namespace android_webview {
AwPrefetchHandleWrapper::AwPrefetchHandleWrapper(
const GURL& url,
std::optional<net::HttpNoVarySearchData> expected_no_vary_search)
: url_(url),
expected_no_vary_search_(std::move(expected_no_vary_search)),
state_(State::kReserved) {
// TODO(crbug.com/452406598): The transition to
// either `kPrePrefetchHandleCommitted` or `kPrefetchHandleCommitted` will be
// strictly bound to the writer interface granting write permission to
// `this`, which ensures the handle is eventually committed.
CheckState();
}
void AwPrefetchHandleWrapper::CommitInitialPrePrefetchHandle(
std::unique_ptr<content::PrePrefetchHandle> pre_prefetch_handle) {
CHECK_EQ(state_, State::kReserved);
// A valid handle should be provided to commit.
CHECK(pre_prefetch_handle);
pre_prefetch_handle_ = std::move(pre_prefetch_handle);
SetState(State::kPrePrefetchHandleCommitted);
}
void AwPrefetchHandleWrapper::CommitInitialPrefetchHandle(
std::unique_ptr<content::PrefetchHandle> prefetch_handle) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
CHECK_EQ(state_, State::kReserved);
// A valid handle should be provided to commit.
CHECK(prefetch_handle);
prefetch_handle_ =
content::CrossThreadPrefetchHandle::Create(std::move(prefetch_handle));
SetState(State::kPrefetchHandleCommitted);
}
AwPrefetchHandleWrapper::AwPrefetchHandleWrapper(
const GURL& url,
std::optional<net::HttpNoVarySearchData> expected_no_vary_search,
std::unique_ptr<content::PrefetchHandle> prefetch_handle)
: url_(url),
expected_no_vary_search_(std::move(expected_no_vary_search)),
prefetch_handle_(content::CrossThreadPrefetchHandle::Create(
std::move(prefetch_handle))),
state_(State::kPrefetchHandleCommitted) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
CHECK(!IsWebViewPrefetchOffTheMainThreadEnabled());
CheckState();
}
AwPrefetchHandleWrapper::~AwPrefetchHandleWrapper() = default;
void AwPrefetchHandleWrapper::CheckState() const {
switch (state_) {
case State::kReserved:
CHECK(!pre_prefetch_handle_);
CHECK(!prefetch_handle_);
break;
case State::kPrePrefetchHandleCommitted:
CHECK(pre_prefetch_handle_);
CHECK(!prefetch_handle_);
break;
case State::kPrePrefetchConsumeStarted:
CHECK(!pre_prefetch_handle_);
CHECK(!prefetch_handle_);
break;
case State::kPrefetchHandleCommitted:
CHECK(!pre_prefetch_handle_);
CHECK(prefetch_handle_);
break;
}
}
std::ostream& operator<<(std::ostream& os,
AwPrefetchHandleWrapper::State state) {
switch (state) {
case AwPrefetchHandleWrapper::State::kReserved:
return os << "kReserved";
case AwPrefetchHandleWrapper::State::kPrePrefetchHandleCommitted:
return os << "kPrePrefetchHandleCommitted";
case AwPrefetchHandleWrapper::State::kPrePrefetchConsumeStarted:
return os << "kPrePrefetchConsumeStarted";
case AwPrefetchHandleWrapper::State::kPrefetchHandleCommitted:
return os << "kPrefetchHandleCommitted";
}
}
void AwPrefetchHandleWrapper::SetState(State new_state) {
{
using T = State;
static const base::NoDestructor<base::StateTransitions<T>> transitions(
base::StateTransitions<T>({
{T::kReserved,
{T::kPrePrefetchHandleCommitted, T::kPrefetchHandleCommitted}},
{T::kPrePrefetchHandleCommitted, {T::kPrePrefetchConsumeStarted}},
{T::kPrePrefetchConsumeStarted, {T::kPrefetchHandleCommitted}},
{T::kPrefetchHandleCommitted, {}},
}));
CHECK_STATE_TRANSITION(transitions, state_, new_state);
}
state_ = new_state;
CheckState();
}
bool AwPrefetchHandleWrapper::CanTakePrePrefetchHandleForConsume() const {
return state_ == State::kPrePrefetchHandleCommitted;
}
std::unique_ptr<content::PrePrefetchHandle>
AwPrefetchHandleWrapper::TakePrePrefetchHandleForConsume() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
CHECK_EQ(state_, State::kPrePrefetchHandleCommitted);
// TODO(crbug.com/452406598): The transition to
// `kPrePrefetchConsumeStarted` will be strictly bound to the writer
// interface granting write permission to `this`, which ensures the
// handle is eventually committed.
auto pre_prefetch_handle = std::move(pre_prefetch_handle_);
SetState(State::kPrePrefetchConsumeStarted);
return pre_prefetch_handle;
}
void AwPrefetchHandleWrapper::CommitPrefetchHandleAfterConsume(
std::unique_ptr<content::PrefetchHandle> prefetch_handle) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
CHECK_EQ(state_, State::kPrePrefetchConsumeStarted);
// A valid handle should be provided to commit.
CHECK(prefetch_handle);
prefetch_handle_ =
content::CrossThreadPrefetchHandle::Create(std::move(prefetch_handle));
SetState(State::kPrefetchHandleCommitted);
}
const GURL& AwPrefetchHandleWrapper::GetURL() const {
return url_;
}
const std::optional<net::HttpNoVarySearchData>&
AwPrefetchHandleWrapper::GetNoVarySearchHint() const {
return expected_no_vary_search_;
}
bool AwPrefetchHandleWrapper::IsPrefetchStale() const {
// We can't touch the inner handle during deduplication, which can happen on
// any thread. Thus, we always return false.
return false;
}
} // namespace android_webview