0

[webview][prefetch] Introduce & Wire PrefetchRequestStatusListener

This CL introduces a listener for certain lifecycle events related
to a prefetch request. Particularly the following:

1. When a prefetch request fails to start (i.e. the URL loader is
never created) for any reason.
2. When a prefetch response completes fetching (after all redirects)
without an error.
3. When there is a non-2XX response code returned from the server.
    - The server response code is also passed back via. the callback.
4. When there is any other error during prefetch response fetching.

All of the above signals will be emitted back to application as either
a prefetch success () or failure (, , or ) and are terminal
WRT to the lifecycle of an individual prefetch request.

Bug: 363946909
Change-Id: I35aa3dedb69ddd9144abc72442acebb1ef7cefa9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5979783
Reviewed-by: Rakina Zata Amni <rakina@chromium.org>
Reviewed-by: Taiyo Mizuhashi <taiyo@chromium.org>
Reviewed-by: Kouhei Ueno <kouhei@chromium.org>
Commit-Queue: Wayne Jackson Jr. <wbjacksonjr@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1383533}
This commit is contained in:
Wayne Jackson Jr.
2024-11-15 11:40:06 +00:00
committed by Chromium LUCI CQ
parent 855053efb8
commit b22d53b23e
16 changed files with 256 additions and 175 deletions

@ -599,9 +599,8 @@ android_library("browser_java") {
"java/src/org/chromium/android_webview/AwNoVarySearchData.java",
"java/src/org/chromium/android_webview/AwPacProcessor.java",
"java/src/org/chromium/android_webview/AwPdfExporter.java",
"java/src/org/chromium/android_webview/AwPrefetchOperationCallback.java",
"java/src/org/chromium/android_webview/AwPrefetchCallback.java",
"java/src/org/chromium/android_webview/AwPrefetchParameters.java",
"java/src/org/chromium/android_webview/AwPrefetchStartResultCode.java",
"java/src/org/chromium/android_webview/AwPrintDocumentAdapter.java",
"java/src/org/chromium/android_webview/AwProxyController.java",
"java/src/org/chromium/android_webview/AwQuotaManagerBridge.java",

@ -74,7 +74,7 @@
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/download_request_utils.h"
#include "content/public/browser/prefetch_browser_callbacks.h"
#include "content/public/browser/prefetch_request_status_listener.h"
#include "content/public/browser/ssl_host_state_delegate.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
@ -100,6 +100,53 @@ using content::BrowserThread;
namespace android_webview {
class AwPrefetchRequestStatusListener
: public content::PrefetchRequestStatusListener {
public:
AwPrefetchRequestStatusListener(
const base::android::ScopedJavaGlobalRef<jobject>
browser_context_java_object,
const base::android::JavaRef<jobject>& callback,
const base::android::JavaRef<jobject>& callback_executor)
: browser_context_java_object_(browser_context_java_object),
prefetch_java_callback_(callback),
prefetch_java_callback_executor_(callback_executor) {}
~AwPrefetchRequestStatusListener() override = default;
void OnPrefetchStartFailed() override {
JNIEnv* env = base::android::AttachCurrentThread();
Java_AwBrowserContext_onPrefetchStartFailed(
env, browser_context_java_object_, prefetch_java_callback_,
prefetch_java_callback_executor_);
}
void OnPrefetchResponseCompleted() override {
JNIEnv* env = base::android::AttachCurrentThread();
Java_AwBrowserContext_onPrefetchResponseCompleted(
env, browser_context_java_object_, prefetch_java_callback_,
prefetch_java_callback_executor_);
}
void OnPrefetchResponseError() override {
JNIEnv* env = base::android::AttachCurrentThread();
Java_AwBrowserContext_onPrefetchResponseError(
env, browser_context_java_object_, prefetch_java_callback_,
prefetch_java_callback_executor_);
}
void OnPrefetchResponseServerError(int response_code) override {
JNIEnv* env = base::android::AttachCurrentThread();
Java_AwBrowserContext_onPrefetchResponseServerError(
env, browser_context_java_object_, prefetch_java_callback_,
prefetch_java_callback_executor_, response_code);
}
private:
base::android::ScopedJavaGlobalRef<jobject> browser_context_java_object_;
base::android::ScopedJavaGlobalRef<jobject> prefetch_java_callback_;
base::android::ScopedJavaGlobalRef<jobject> prefetch_java_callback_executor_;
};
namespace {
const void* const kDownloadManagerDelegateKey = &kDownloadManagerDelegateKey;
@ -707,40 +754,19 @@ void AwBrowserContext::StartPrefetchRequest(
DCHECK_CURRENTLY_ON(BrowserThread::UI);
GURL pf_url = GURL(url);
net::HttpRequestHeaders pf_additional_headers =
net::HttpRequestHeaders additional_headers =
GetAdditionalHeadersFromPrefetchParameters(env, prefetch_params);
std::optional<net::HttpNoVarySearchData> pf_expected_no_vary_search =
std::optional<net::HttpNoVarySearchData> expected_no_vary_search =
GetExpectedNoVarySearchFromPrefetchParameters(env, prefetch_params);
base::android::ScopedJavaGlobalRef<jobject> pf_callback(callback);
base::android::ScopedJavaGlobalRef<jobject> pf_callback_executor(
callback_executor);
content::PrefetchStartCallback pf_start_callback =
base::BindOnce(&AwBrowserContext::HandlePrefetchStartCallback,
weak_method_factory_.GetWeakPtr(), std::move(pf_callback),
std::move(pf_callback_executor));
std::unique_ptr<content::PrefetchRequestStatusListener>
request_status_listener =
std::make_unique<AwPrefetchRequestStatusListener>(obj_, callback,
callback_executor);
StartBrowserPrefetchRequest(
pf_url,
GetIsJavaScriptEnabledFromPrefetchParameters(env, prefetch_params),
pf_expected_no_vary_search, pf_additional_headers,
std::move(pf_start_callback));
}
void AwBrowserContext::HandlePrefetchStartCallback(
const base::android::ScopedJavaGlobalRef<jobject> callback,
const base::android::ScopedJavaGlobalRef<jobject> callback_executor,
const content::PrefetchStartResultCode result_code) {
JNIEnv* env = base::android::AttachCurrentThread();
switch (result_code) {
case content::PrefetchStartResultCode::kSuccess:
Java_AwBrowserContext_onPrefetchStarted(env, obj_, callback,
callback_executor);
break;
case content::PrefetchStartResultCode::kFailed:
Java_AwBrowserContext_onPrefetchStartFailed(env, obj_, callback,
callback_executor);
break;
}
expected_no_vary_search, additional_headers,
std::move(request_status_listener));
}
std::unique_ptr<AwContentsIoThreadClient>

@ -30,7 +30,6 @@
#include "components/prefs/pref_registry_simple.h"
#include "components/visitedlink/browser/visitedlink_delegate.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/prefetch_browser_callbacks.h"
#include "content/public/browser/zoom_level_delegate.h"
#include "net/http/http_request_headers.h"
#include "services/cert_verifier/public/mojom/cert_verifier_service_factory.mojom-forward.h"
@ -199,11 +198,6 @@ class AwBrowserContext : public content::BrowserContext,
std::unique_ptr<AwContentsIoThreadClient>
GetServiceWorkerIoThreadClientThreadSafe();
void HandlePrefetchStartCallback(
const base::android::ScopedJavaGlobalRef<jobject> callback,
const base::android::ScopedJavaGlobalRef<jobject> callback_executor,
const content::PrefetchStartResultCode result_code);
const std::string name_;
const base::FilePath relative_path_;
const bool is_default_;

@ -4,7 +4,12 @@
package com.android.webview.chromium;
import org.chromium.android_webview.AwPrefetchStartResultCode;
import android.os.Bundle;
import androidx.annotation.Nullable;
import org.chromium.android_webview.AwPrefetchCallback;
import org.chromium.android_webview.AwPrefetchCallback.StatusCode;
import org.chromium.android_webview.common.Lifetime;
@Lifetime.Temporary
@ -16,19 +21,26 @@ public class PrefetchOperationResult {
this.statusCode = statusCode;
}
public static PrefetchOperationResult fromStartResultCode(
@AwPrefetchStartResultCode int startResultCode) {
int statusCode;
switch (startResultCode) {
case AwPrefetchStartResultCode.SUCCESS:
statusCode = PrefetchOperationStatusCode.SUCCESS;
break;
case AwPrefetchStartResultCode.FAILURE:
statusCode = PrefetchOperationStatusCode.FAILURE;
break;
@Nullable
public static PrefetchOperationResult fromPrefetchStatusCode(
@StatusCode int statusCode, @Nullable Bundle extras) {
// TODO(crbug.com/372915075) : Implement tests.
switch (statusCode) {
case StatusCode.PREFETCH_RESPONSE_COMPLETED:
return new PrefetchOperationResult(PrefetchOperationStatusCode.SUCCESS);
case StatusCode.PREFETCH_START_FAILED:
case StatusCode.PREFETCH_RESPONSE_GENERIC_ERROR:
return new PrefetchOperationResult(PrefetchOperationStatusCode.FAILURE);
case StatusCode.PREFETCH_RESPONSE_SERVER_ERROR:
if (extras != null
&& extras.containsKey(AwPrefetchCallback.EXTRA_HTTP_RESPONSE_CODE)) {
// TODO(crbug.com/378481147) : Return the HTTP response code via. prefetch
// exception.
}
return new PrefetchOperationResult(PrefetchOperationStatusCode.FAILURE);
default:
throw new IllegalArgumentException("Invalid prefetch start result code");
throw new IllegalArgumentException(
"Unhandled or invalid prefetch status code - status_code=" + statusCode);
}
return new PrefetchOperationResult(statusCode);
}
}

@ -4,6 +4,7 @@
package com.android.webview.chromium;
import android.os.Bundle;
import android.webkit.CookieManager;
import android.webkit.GeolocationPermissions;
import android.webkit.ServiceWorkerController;
@ -15,9 +16,8 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.chromium.android_webview.AwBrowserContext;
import org.chromium.android_webview.AwPrefetchOperationCallback;
import org.chromium.android_webview.AwPrefetchCallback;
import org.chromium.android_webview.AwPrefetchParameters;
import org.chromium.android_webview.AwPrefetchStartResultCode;
import org.chromium.android_webview.common.Lifetime;
import org.chromium.base.ThreadUtils;
import org.chromium.base.TraceEvent;
@ -106,12 +106,17 @@ public class Profile {
AwPrefetchParameters awPrefetchParameters =
params == null ? null : params.toAwPrefetchParams();
AwPrefetchOperationCallback<Integer> awCallback =
new AwPrefetchOperationCallback<>() {
AwPrefetchCallback awCallback =
new AwPrefetchCallback() {
@Override
public void onResult(@AwPrefetchStartResultCode Integer result) {
resultCallback.onReceiveValue(
PrefetchOperationResult.fromStartResultCode(result));
public void onStatusUpdated(
@StatusCode int statusCode, @Nullable Bundle extras) {
PrefetchOperationResult operationResult =
PrefetchOperationResult.fromPrefetchStatusCode(
statusCode, extras);
if (operationResult != null) {
resultCallback.onReceiveValue(operationResult);
}
}
@Override

@ -6,6 +6,7 @@ package org.chromium.android_webview;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.LruCache;
import androidx.annotation.NonNull;
@ -18,6 +19,7 @@ import org.jni_zero.JNINamespace;
import org.jni_zero.JniType;
import org.jni_zero.NativeMethods;
import org.chromium.android_webview.AwPrefetchCallback.StatusCode;
import org.chromium.android_webview.common.Lifetime;
import org.chromium.android_webview.common.MediaIntegrityApiStatus;
import org.chromium.android_webview.common.MediaIntegrityProvider;
@ -286,7 +288,7 @@ public class AwBrowserContext implements BrowserContextHandle {
public void startPrefetchRequest(
@NonNull String url,
@Nullable AwPrefetchParameters prefetchParameters,
@NonNull AwPrefetchOperationCallback<Integer> callback,
@NonNull AwPrefetchCallback callback,
@NonNull Executor callbackExecutor) {
assert ThreadUtils.runningOnUiThread();
if (!UrlUtilities.isHttps(url)) {
@ -318,15 +320,31 @@ public class AwBrowserContext implements BrowserContextHandle {
}
@CalledByNative
public void onPrefetchStarted(
AwPrefetchOperationCallback<Integer> callback, Executor callbackExecutor) {
callbackExecutor.execute(() -> callback.onResult(AwPrefetchStartResultCode.SUCCESS));
public void onPrefetchStartFailed(AwPrefetchCallback callback, Executor callbackExecutor) {
callbackExecutor.execute(
() -> callback.onStatusUpdated(StatusCode.PREFETCH_START_FAILED, null));
}
@CalledByNative
public void onPrefetchStartFailed(
AwPrefetchOperationCallback<Integer> callback, Executor callbackExecutor) {
callbackExecutor.execute(() -> callback.onResult(AwPrefetchStartResultCode.FAILURE));
public void onPrefetchResponseCompleted(
AwPrefetchCallback callback, Executor callbackExecutor) {
callbackExecutor.execute(
() -> callback.onStatusUpdated(StatusCode.PREFETCH_RESPONSE_COMPLETED, null));
}
@CalledByNative
public void onPrefetchResponseError(AwPrefetchCallback callback, Executor callbackExecutor) {
callbackExecutor.execute(
() -> callback.onStatusUpdated(StatusCode.PREFETCH_RESPONSE_GENERIC_ERROR, null));
}
@CalledByNative
public void onPrefetchResponseServerError(
AwPrefetchCallback callback, Executor callbackExecutor, int httpResponseCode) {
Bundle extras = new Bundle();
extras.putInt(AwPrefetchCallback.EXTRA_HTTP_RESPONSE_CODE, httpResponseCode);
callbackExecutor.execute(
() -> callback.onStatusUpdated(StatusCode.PREFETCH_RESPONSE_SERVER_ERROR, extras));
}
private void migrateGeolocationPreferences() {
@ -460,7 +478,7 @@ public class AwBrowserContext implements BrowserContextHandle {
long nativeAwBrowserContext,
@JniType("std::string") String url,
AwPrefetchParameters prefetchParameters,
AwPrefetchOperationCallback<Integer> callback,
AwPrefetchCallback callback,
Executor callbackExecutor);
}
}

@ -0,0 +1,36 @@
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.android_webview;
import android.os.Bundle;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/** Callback for WebView app initiated prefetching. */
public interface AwPrefetchCallback {
@IntDef({
StatusCode.PREFETCH_START_FAILED,
StatusCode.PREFETCH_RESPONSE_COMPLETED,
StatusCode.PREFETCH_RESPONSE_SERVER_ERROR,
StatusCode.PREFETCH_RESPONSE_GENERIC_ERROR
})
@Retention(RetentionPolicy.SOURCE)
public @interface StatusCode {
int PREFETCH_START_FAILED = 0;
int PREFETCH_RESPONSE_COMPLETED = 1;
int PREFETCH_RESPONSE_SERVER_ERROR = 2;
int PREFETCH_RESPONSE_GENERIC_ERROR = 3;
}
public static final String EXTRA_HTTP_RESPONSE_CODE = "HttpResponseCode";
void onStatusUpdated(@StatusCode int statusCode, @Nullable Bundle extras);
void onError(Throwable e);
}

@ -1,16 +0,0 @@
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.android_webview;
/**
* A set of callbacks related to WebView prefetching operations.
*
* @param <T> the result type.
*/
public interface AwPrefetchOperationCallback<T> {
void onResult(T result);
void onError(Throwable e);
}

@ -1,17 +0,0 @@
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.android_webview;
import androidx.annotation.IntDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@IntDef({AwPrefetchStartResultCode.SUCCESS, AwPrefetchStartResultCode.FAILURE})
@Retention(RetentionPolicy.SOURCE)
public @interface AwPrefetchStartResultCode {
int SUCCESS = 0;
int FAILURE = 1;
}

@ -50,7 +50,6 @@
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/download_manager.h"
#include "content/public/browser/permission_controller.h"
#include "content/public/browser/prefetch_browser_callbacks.h"
#include "content/public/browser/preloading_trigger_type.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/site_instance.h"
@ -196,15 +195,14 @@ void BrowserContext::StartBrowserPrefetchRequest(
bool javascript_enabled,
std::optional<net::HttpNoVarySearchData> no_vary_search_expected,
const net::HttpRequestHeaders& additional_headers,
std::optional<PrefetchStartCallback> prefetch_start_callback) {
std::unique_ptr<PrefetchRequestStatusListener> request_status_listener) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
PrefetchService* prefetch_service =
BrowserContextImpl::From(this)->GetPrefetchService();
if (!prefetch_service) {
if (prefetch_start_callback.has_value()) {
std::move(prefetch_start_callback.value())
.Run(PrefetchStartResultCode::kFailed);
if (request_status_listener) {
request_status_listener->OnPrefetchStartFailed();
}
return;
}
@ -215,7 +213,7 @@ void BrowserContext::StartBrowserPrefetchRequest(
this, url, prefetch_type, blink::mojom::Referrer(), javascript_enabled,
/*referring_origin=*/std::nullopt, std::move(no_vary_search_expected),
/*attempt=*/nullptr, additional_headers,
std::move(prefetch_start_callback));
std::move(request_status_listener));
prefetch_service->AddPrefetchContainer(std::move(container));
}

@ -39,7 +39,7 @@
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/client_hints.h"
#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/prefetch_browser_callbacks.h"
#include "content/public/browser/prefetch_request_status_listener.h"
#include "content/public/browser/preloading.h"
#include "content/public/browser/web_contents.h"
#include "net/base/load_flags.h"
@ -415,7 +415,7 @@ PrefetchContainer::PrefetchContainer(
/*holdback_status_override=*/std::nullopt,
referring_render_frame_host.GetDevToolsNavigationToken(),
/*Must be empty: additional_headers=*/{},
/*prefetch_start_callback=*/std::nullopt,
/*request_status_listener=*/nullptr,
WebContentsImpl::FromRenderFrameHostImpl(&referring_render_frame_host)
->GetOrCreateWebPreferences()
.javascript_enabled) {
@ -449,7 +449,7 @@ PrefetchContainer::PrefetchContainer(
holdback_status_override,
/*initiator_devtools_navigation_token=*/std::nullopt,
/*Must be empty: additional_headers=*/{},
/*prefetch_start_callback=*/std::nullopt,
/*request_status_listener=*/nullptr,
referring_web_contents.GetOrCreateWebPreferences()
.javascript_enabled) {
CHECK(!prefetch_type_.IsRendererInitiated());
@ -466,7 +466,7 @@ PrefetchContainer::PrefetchContainer(
std::optional<net::HttpNoVarySearchData> no_vary_search_hint,
base::WeakPtr<PreloadingAttempt> attempt,
const net::HttpRequestHeaders& additional_headers,
std::optional<PrefetchStartCallback> prefetch_start_callback)
std::unique_ptr<PrefetchRequestStatusListener> request_status_listener)
: PrefetchContainer(GlobalRenderFrameHostId(),
referring_origin,
/*referring_url_hash=*/std::nullopt,
@ -484,7 +484,7 @@ PrefetchContainer::PrefetchContainer(
/*holdback_status_override=*/std::nullopt,
/*initiator_devtools_navigation_token=*/std::nullopt,
additional_headers,
std::move(prefetch_start_callback),
std::move(request_status_listener),
javascript_enabled) {
CHECK(!prefetch_type_.IsRendererInitiated());
CHECK(PrefetchBrowserInitiatedTriggersEnabled());
@ -506,7 +506,7 @@ PrefetchContainer::PrefetchContainer(
std::optional<PreloadingHoldbackStatus> holdback_status_override,
std::optional<base::UnguessableToken> initiator_devtools_navigation_token,
const net::HttpRequestHeaders& additional_headers,
std::optional<PrefetchStartCallback> prefetch_start_callback,
std::unique_ptr<PrefetchRequestStatusListener> request_status_listener,
bool is_javascript_enabled)
: referring_render_frame_host_id_(referring_render_frame_host_id),
referring_origin_(referring_origin),
@ -525,7 +525,7 @@ PrefetchContainer::PrefetchContainer(
initiator_devtools_navigation_token_(
std::move(initiator_devtools_navigation_token)),
additional_headers_(additional_headers),
prefetch_start_callback_(std::move(prefetch_start_callback)),
request_status_listener_(std::move(request_status_listener)),
is_javascript_enabled_(is_javascript_enabled) {
is_likely_ahead_of_prerender_ =
CalculateIsLikelyAheadOfPrerender(attempt_.get());
@ -1421,13 +1421,32 @@ void PrefetchContainer::OnPrefetchComplete(
RecordPrefetchProxyPrefetchMainframeBodyLength(body_length);
}
if (GetPrefetchStatus() == PrefetchStatus::kPrefetchSuccessful) {
const PrefetchStatus prefetch_status = GetPrefetchStatus();
if (prefetch_status == PrefetchStatus::kPrefetchSuccessful &&
IsRendererInitiated()) {
// TODO(crbug.com/40946257): Current code doesn't support
// PrefetchReferringPageMetrics when the prefetch is initiated by browser.
if (IsRendererInitiated()) {
if (prefetch_document_manager_) {
prefetch_document_manager_->OnPrefetchSuccessful(this);
if (prefetch_document_manager_) {
prefetch_document_manager_->OnPrefetchSuccessful(this);
}
}
if (request_status_listener_) {
switch (prefetch_status) {
case PrefetchStatus::kPrefetchSuccessful:
case PrefetchStatus::kPrefetchResponseUsed:
request_status_listener_->OnPrefetchResponseCompleted();
break;
case PrefetchStatus::kPrefetchFailedNon2XX: {
int response_code = GetNonRedirectHead()
? GetNonRedirectHead()->headers->response_code()
: 0;
request_status_listener_->OnPrefetchResponseServerError(response_code);
break;
}
default:
request_status_listener_->OnPrefetchResponseError();
break;
}
}
}
@ -1633,11 +1652,6 @@ void PrefetchContainer::OnDetectedCookiesChange2() {
void PrefetchContainer::OnPrefetchStarted() {
SetLoadState(PrefetchContainer::LoadState::kStarted);
if (prefetch_start_callback_.has_value()) {
CHECK(prefetch_start_callback_.value());
std::move(prefetch_start_callback_.value())
.Run(PrefetchStartResultCode::kSuccess);
}
}
// TODO(crbug.com/40274818): We might be waiting on PrefetchContainer's head
@ -2027,20 +2041,11 @@ void PrefetchContainer::OnInitialPrefetchFailedIneligible(
PreloadingEligibility eligibility) {
CHECK(redirect_chain_.size() == 1);
CHECK_NE(eligibility, PreloadingEligibility::kEligible);
if (prefetch_start_callback_.has_value()) {
CHECK(prefetch_start_callback_.value());
std::move(prefetch_start_callback_.value())
.Run(GetPrefetchFailedIneligibleStartResultCode(eligibility));
if (request_status_listener_) {
request_status_listener_->OnPrefetchStartFailed();
}
}
PrefetchStartResultCode
PrefetchContainer::GetPrefetchFailedIneligibleStartResultCode(
PreloadingEligibility eligibility) {
CHECK_NE(eligibility, PreloadingEligibility::kEligible);
return PrefetchStartResultCode::kFailed;
}
void PrefetchContainer::AddObserver(Observer* observer) {
CHECK(UseNewWaitLoop());

@ -21,7 +21,7 @@
#include "content/browser/preloading/speculation_host_devtools_observer.h"
#include "content/common/content_export.h"
#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/prefetch_browser_callbacks.h"
#include "content/public/browser/prefetch_request_status_listener.h"
#include "content/public/browser/preloading.h"
#include "content/public/browser/preloading_data.h"
#include "net/http/http_no_vary_search_data.h"
@ -137,8 +137,8 @@ class CONTENT_EXPORT PrefetchContainer {
std::optional<net::HttpNoVarySearchData> no_vary_search_expected,
base::WeakPtr<PreloadingAttempt> attempt = nullptr,
const net::HttpRequestHeaders& additional_headers = {},
std::optional<PrefetchStartCallback> prefetch_start_callback =
std::nullopt);
std::unique_ptr<PrefetchRequestStatusListener> request_status_listener =
nullptr);
~PrefetchContainer();
@ -789,7 +789,7 @@ class CONTENT_EXPORT PrefetchContainer {
std::optional<PreloadingHoldbackStatus> holdback_status_override,
std::optional<base::UnguessableToken> initiator_devtools_navigation_token,
const net::HttpRequestHeaders& additional_headers,
std::optional<PrefetchStartCallback> prefetch_start_callback,
std::unique_ptr<PrefetchRequestStatusListener> request_status_listener,
bool is_javascript_enabled);
// Update |prefetch_status_| and report prefetch status to
@ -827,10 +827,6 @@ class CONTENT_EXPORT PrefetchContainer {
// redirects.
void OnInitialPrefetchFailedIneligible(PreloadingEligibility eligibility);
// Returns the |PrefetchStartResultCode| based on the |eligibility|.
PrefetchStartResultCode GetPrefetchFailedIneligibleStartResultCode(
PreloadingEligibility eligibility);
// Record `prefetch_status` to UMA if it hasn't already been recorded for this
// container.
// Note: We use a parameter instead of just `prefetch_status_` as it may not
@ -1021,8 +1017,9 @@ class CONTENT_EXPORT PrefetchContainer {
// TODO(crbug.com/369859822): Revisit the semantics if needed.
const net::HttpRequestHeaders additional_headers_;
// Browser callbacks.
std::optional<PrefetchStartCallback> prefetch_start_callback_;
// Listener of prefetch request. Currently used for WebView initiated
// prefetch.
std::unique_ptr<PrefetchRequestStatusListener> request_status_listener_;
std::unique_ptr<base::OneShotTimer> timeout_timer_;

@ -318,8 +318,8 @@ source_set("browser_sources") {
"picture_in_picture_window_controller.h",
"platform_notification_context.h",
"platform_notification_service.h",
"prefetch_browser_callbacks.h",
"prefetch_metrics.h",
"prefetch_request_status_listener.h",
"prefetch_service_delegate.cc",
"prefetch_service_delegate.h",
"preloading.h",

@ -21,7 +21,7 @@
#include "base/supports_user_data.h"
#include "content/common/content_export.h"
#include "content/public/browser/k_anonymity_service_delegate.h"
#include "content/public/browser/prefetch_browser_callbacks.h"
#include "content/public/browser/prefetch_request_status_listener.h"
#include "content/public/browser/zoom_level_delegate.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
@ -207,7 +207,7 @@ class CONTENT_EXPORT BrowserContext : public base::SupportsUserData {
bool javascript_enabled,
std::optional<net::HttpNoVarySearchData> no_vary_search_expected,
const net::HttpRequestHeaders& additional_headers,
std::optional<PrefetchStartCallback> prefetch_start_callback);
std::unique_ptr<PrefetchRequestStatusListener> request_status_listener);
using BlobCallback = base::OnceCallback<void(std::unique_ptr<BlobHandle>)>;
using BlobContextGetter =

@ -1,30 +0,0 @@
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_PUBLIC_BROWSER_PREFETCH_BROWSER_CALLBACKS_H_
#define CONTENT_PUBLIC_BROWSER_PREFETCH_BROWSER_CALLBACKS_H_
#include "base/functional/callback.h"
#include "base/functional/callback_forward.h"
namespace content {
enum class PrefetchStartResultCode {
// Prefetch was started successfully (i.e. the URL loader was created &
// started).
kSuccess = 0,
// Prefetch failed to start.
kFailed = 1,
};
// Used to report when a prefetch request has either started (i.e. the URL
// loader has been created & started) or failed to start. This interface is
// experimental and is subject to change, therefore it is not recommended to use
// this outside of WebView app initiated prefetching.
using PrefetchStartCallback =
base::OnceCallback<void(const PrefetchStartResultCode)>;
} // namespace content
#endif // CONTENT_PUBLIC_BROWSER_PREFETCH_BROWSER_CALLBACKS_H_

@ -0,0 +1,54 @@
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_PUBLIC_BROWSER_PREFETCH_REQUEST_STATUS_LISTENER_H_
#define CONTENT_PUBLIC_BROWSER_PREFETCH_REQUEST_STATUS_LISTENER_H_
namespace content {
// Callback interface to receive the status of a prefetch request.
// The callback lifecycle is as follows:
//
// 1. If the prefetch request fails to start (i.e. before the URL loader is
// created & started) for any reason
// |PrefetchStatusListener::OnPrefetchStartFailed()| is called. This is a
// terminal signal (i.e. no more callback signals will emit for "this" prefetch
// request).
//
// 2. After the prefetch request starts (i.e. the URL loader is created and
// started) one of the following callbacks will be called (mutually exclusive):
//
// 2a. |PrefetchStatusListener::OnPrefetchResponseCompleted()| if the response
// was fetched successfully or has been served to a navigation (after all
// redirects).
//
// 2b. |PrefetchStatusListener::OnPrefetchResponseError()| if the server
// responds with a non 2XX response code (after all redirects).
//
// 2c. |PrefetchStatusListener::OnPrefetchResponseError()| if any other error
// occurs during the response fetching (after all redirects).
class PrefetchRequestStatusListener {
public:
virtual ~PrefetchRequestStatusListener() = default;
// Called when the prefetch request failed to start.
virtual void OnPrefetchStartFailed() = 0;
// Called when the prefetch response has been fetched.
// This is only emitted for the final response (i.e. after all redirects).
virtual void OnPrefetchResponseCompleted() = 0;
// Called when there is an error with the prefetch response that is not server
// related.
virtual void OnPrefetchResponseError() = 0;
// Called when the server returns an error status code for a prefetch request
// (e.g. 500). The |response_code| is the HTTP response code returned from
// the server.
virtual void OnPrefetchResponseServerError(int response_code) = 0;
};
} // namespace content
#endif // CONTENT_PUBLIC_BROWSER_PREFETCH_REQUEST_STATUS_LISTENER_H_