blob: 5f621e7836bcaf0d9f5da8454a57fafa6e4a3d33 [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.
#include "android_webview/browser/aw_preconnector.h"
#include <jni.h>
#include <memory>
#include <optional>
#include "base/trace_event/trace_event.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/preconnect_manager.h"
#include "content/public/browser/preconnect_request.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "net/base/network_anonymization_key.h"
#include "url/android/gurl_android.h"
#include "url/origin.h"
#include "url/url_constants.h"
// Has to come after all the FromJniType() / ToJniType() headers.
#include "android_webview/browser_jni_headers/AwPreconnector_jni.h"
namespace {
inline constexpr net::NetworkTrafficAnnotationTag
kWebViewPreconnectTrafficAnnotation =
net::DefineNetworkTrafficAnnotation("webview_preconnect",
R"(
semantics {
sender: "Android WebView"
description: "WebView is an Android component that allows Android "
"applications to render web contents in their app. The WebView "
"preconnect API allows apps to open a connection to a domain "
"before loading any pages to speed up future loads."
trigger: "This is triggered when an application uses WebView's "
"Profile#preconnect API. It is up to the Android developer to "
"decide when to call this."
internal {
contacts {
owners: "//android_webview/OWNERS"
}
}
user_data {
type: ACCESS_TOKEN
}
data: "None"
destination: WEBSITE
last_reviewed: "2025-08-01"
}
policy {
cookies_allowed: NO
setting: "Not user controlled"
policy_exception_justification:
"No data is sent beyond what is included in a normal page load "
"triggered by WebView#loadUrl. This API is purely an optimization "
", allowing the opening of the network request to be moved "
"earlier."
}
)");
} // anonymous namespace
namespace android_webview {
AwPreconnector::AwPreconnector(content::BrowserContext* browser_context)
: browser_context_(browser_context) {}
AwPreconnector::~AwPreconnector() {
if (java_obj_) {
JNIEnv* env = base::android::AttachCurrentThread();
Java_AwPreconnector_destroy(env, java_obj_);
}
}
void AwPreconnector::Preconnect(JNIEnv* env, const GURL& url) {
// Network anonymization isn't implemented for WebView, so we can use an empty
// key.
net::NetworkAnonymizationKey key = net::NetworkAnonymizationKey();
if (!url.is_valid()) {
return;
}
url::Origin origin = url::Origin::Create(url);
if ((origin.scheme() != url::kHttpScheme) &&
(origin.scheme() != url::kHttpsScheme)) {
// Cannot preconnect to local or opaque origins.
return;
}
std::vector<content::PreconnectRequest> requests = {
content::PreconnectRequest(origin, /* num_sockets= */ 1, key)};
GetPreconnectManager().Start(url, requests,
kWebViewPreconnectTrafficAnnotation);
TRACE_EVENT1("android_webview", "Preconnect::Begin", "url", url);
}
void AwPreconnector::PreconnectInitiated(const GURL& url,
const GURL& preconnect_url) {}
void AwPreconnector::PreconnectFinished(
std::unique_ptr<content::PreconnectStats> stats) {
TRACE_EVENT1("android_webview", "Preconnect::Finished", "url", stats->url);
}
bool AwPreconnector::IsPreconnectEnabled() {
return true;
}
base::android::ScopedJavaLocalRef<jobject>
AwPreconnector::GetJavaAwPreconnector() {
if (!java_obj_) {
JNIEnv* env = base::android::AttachCurrentThread();
java_obj_ =
Java_AwPreconnector_create(env, reinterpret_cast<intptr_t>(this));
}
return base::android::ScopedJavaLocalRef<jobject>(java_obj_);
}
content::PreconnectManager& AwPreconnector::GetPreconnectManager() {
if (!preconnect_manager_) {
preconnect_manager_ = content::PreconnectManager::Create(
weak_factory_.GetWeakPtr(), browser_context_);
}
return *preconnect_manager_.get();
}
} // namespace android_webview