blob: be58852626ec3335825117a2793ee24335fbe93e [file] [log] [blame]
// Copyright 2012 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_browser_context.h"
#include <jni.h>
#include <algorithm>
#include <iterator>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include "android_webview/browser/aw_browser_context_store.h"
#include "android_webview/browser/aw_browser_process.h"
#include "android_webview/browser/aw_client_hints_controller_delegate.h"
#include "android_webview/browser/aw_content_browser_client.h"
#include "android_webview/browser/aw_contents_origin_matcher.h"
#include "android_webview/browser/aw_download_manager_delegate.h"
#include "android_webview/browser/aw_form_database_service.h"
#include "android_webview/browser/aw_origin_matched_header.h"
#include "android_webview/browser/aw_permission_manager.h"
#include "android_webview/browser/aw_quota_manager_bridge.h"
#include "android_webview/browser/aw_web_ui_controller_factory.h"
#include "android_webview/browser/cookie_manager.h"
#include "android_webview/browser/metrics/aw_metrics_service_client.h"
#include "android_webview/browser/network_service/net_helpers.h"
#include "android_webview/browser/prefetch/aw_preloading_utils.h"
#include "android_webview/browser/safe_browsing/aw_safe_browsing_allowlist_manager.h"
#include "android_webview/common/aw_features.h"
#include "android_webview/common/aw_switches.h"
#include "android_webview/common/crash_reporter/crash_keys.h"
#include "base/android/callback_android.h"
#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h"
#include "base/base_paths_posix.h"
#include "base/check_op.h"
#include "base/command_line.h"
#include "base/containers/map_util.h"
#include "base/feature_list.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/scoped_refptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/path_service.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/threading/thread_restrictions.h"
#include "components/autofill/core/common/autofill_prefs.h"
#include "components/cdm/browser/media_drm_storage_impl.h"
#include "components/download/public/common/in_progress_download_manager.h"
#include "components/keyed_service/core/simple_key_map.h"
#include "components/origin_matcher/origin_matcher.h"
#include "components/origin_trials/browser/leveldb_persistence_provider.h"
#include "components/origin_trials/browser/origin_trials.h"
#include "components/policy/core/browser/browser_policy_connector_base.h"
#include "components/policy/core/browser/configuration_policy_pref_store.h"
#include "components/policy/core/browser/url_blocklist_manager.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/in_memory_pref_store.h"
#include "components/prefs/json_pref_store.h"
#include "components/prefs/pref_name_set.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/pref_service_factory.h"
#include "components/prefs/segregated_pref_store.h"
#include "components/profile_metrics/browser_profile_type.h"
#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
#include "components/url_formatter/url_fixer.h"
#include "components/user_prefs/user_prefs.h"
#include "components/visitedlink/browser/visitedlink_writer.h"
#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/render_process_host.h"
#include "content/public/browser/spare_render_process_host_manager.h"
#include "content/public/browser/ssl_host_state_delegate.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/zoom_level_delegate.h"
#include "media/mojo/buildflags.h"
#include "net/http/http_no_vary_search_data.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_util.h"
#include "net/proxy_resolution/proxy_config_service_android.h"
#include "net/proxy_resolution/proxy_resolution_service.h"
#include "services/cert_verifier/public/mojom/cert_verifier_service_factory.mojom.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "services/network/public/mojom/url_loader.mojom.h"
#include "third_party/blink/public/common/origin_trials/trial_token_validator.h"
#include "url/android/gurl_android.h"
#include "url/gurl.h"
// Must come after all headers that specialize FromJniType() / ToJniType().
#include "android_webview/browser_jni_headers/AwBrowserContext_jni.h"
using base::FilePath;
using content::BrowserThread;
namespace android_webview {
namespace {
const void* const kDownloadManagerDelegateKey = &kDownloadManagerDelegateKey;
// Empty method to skip origin security check as DownloadManager will set its
// own method.
bool IgnoreOriginSecurityCheck(const GURL& url) {
return true;
}
void MigrateProfileData(base::FilePath cache_path,
base::FilePath context_storage_path) {
TRACE_EVENT0("startup", "MigrateProfileData");
bool migration_happened = false;
FilePath old_cache_path;
base::PathService::Get(base::DIR_CACHE, &old_cache_path);
old_cache_path = old_cache_path.DirName().Append(
FILE_PATH_LITERAL("org.chromium.android_webview"));
if (base::PathExists(old_cache_path)) {
migration_happened = true;
bool success = base::CreateDirectory(cache_path);
if (success)
success &= base::Move(old_cache_path, cache_path);
DCHECK(success);
}
base::FilePath old_context_storage_path;
base::PathService::Get(base::DIR_ANDROID_APP_DATA, &old_context_storage_path);
if (!base::PathExists(context_storage_path)) {
base::CreateDirectory(context_storage_path);
}
auto migrate_context_storage_data = [&old_context_storage_path,
&context_storage_path,
&migration_happened](auto& suffix) {
FilePath old_file = old_context_storage_path.Append(suffix);
if (base::PathExists(old_file)) {
migration_happened = true;
FilePath new_file = context_storage_path.Append(suffix);
if (base::PathExists(new_file)) {
bool success =
base::Move(new_file, new_file.AddExtension(".partial-migration"));
DCHECK(success);
}
bool success = base::Move(old_file, new_file);
DCHECK(success);
}
};
// These were handled in the initial migration
migrate_context_storage_data("Web Data");
migrate_context_storage_data("Web Data-journal");
migrate_context_storage_data("GPUCache");
migrate_context_storage_data("blob_storage");
migrate_context_storage_data("Session Storage");
// These were missed in the initial migration
migrate_context_storage_data("File System");
migrate_context_storage_data("IndexedDB");
migrate_context_storage_data("Local Storage");
migrate_context_storage_data("QuotaManager");
migrate_context_storage_data("QuotaManager-journal");
migrate_context_storage_data("Service Worker");
migrate_context_storage_data("VideoDecodeStats");
migrate_context_storage_data("shared_proto_db");
migrate_context_storage_data("webrtc_event_logs");
base::UmaHistogramBoolean(
"Android.WebView.AwBrowserContext.ProfileDataMigrationHappened",
migration_happened);
}
base::FilePath BuildCachePath(const base::FilePath& relative_path) {
FilePath cache_path;
if (!base::PathService::Get(base::DIR_CACHE, &cache_path)) {
NOTREACHED() << "Failed to get app cache directory for Android WebView";
}
return cache_path.Append(relative_path);
}
base::FilePath BuildHttpCachePath(const base::FilePath& relative_path) {
return BuildCachePath(relative_path).Append(FILE_PATH_LITERAL("HTTP Cache"));
}
} // namespace
AwBrowserContext::AwBrowserContext(std::string name,
base::FilePath relative_path,
const bool is_default)
: name_(std::move(name)),
relative_path_(std::move(relative_path)),
is_default_(is_default),
context_storage_path_(BuildStoragePath(relative_path_)),
http_cache_path_(BuildHttpCachePath(relative_path_)),
simple_factory_key_(GetPath(), IsOffTheRecord()) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
TRACE_EVENT("startup", "AwBrowserContext::AwBrowserContext", "name", name_);
profile_metrics::SetBrowserProfileType(
this, profile_metrics::BrowserProfileType::kRegular);
if (IsDefaultBrowserContext()) {
MigrateProfileData(GetHttpCachePath(), GetPath());
} else {
cookie_manager_ = std::make_unique<CookieManager>(this);
}
SimpleKeyMap::GetInstance()->Associate(this, &simple_factory_key_);
CreateUserPrefService();
visitedlink_writer_ =
std::make_unique<visitedlink::VisitedLinkWriter>(this, this, false);
visitedlink_writer_->Init();
form_database_service_ =
std::make_unique<AwFormDatabaseService>(context_storage_path_);
EnsureResourceContextInitialized();
prefetch_manager_ = std::make_unique<AwPrefetchManager>(this);
preconnector_ = std::make_unique<AwPreconnector>(this);
// This should be initialized as soon as possible when creating the profile,
// in order to load the database from disk.
origin_trials_controller_delegate_ =
std::make_unique<origin_trials::OriginTrials>(
std::make_unique<origin_trials::LevelDbPersistenceProvider>(
GetPath(),
GetDefaultStoragePartition()->GetProtoDatabaseProvider()),
std::make_unique<blink::TrialTokenValidator>());
}
AwBrowserContext::~AwBrowserContext() {
NotifyWillBeDestroyed();
SimpleKeyMap::GetInstance()->Dissociate(this);
ShutdownStoragePartitions();
}
// static
AwBrowserContext* AwBrowserContext::GetDefault() {
return AwBrowserContextStore::GetInstance()->GetDefault();
}
// static
AwBrowserContext* AwBrowserContext::FromWebContents(
content::WebContents* web_contents) {
// This cast is safe; this is the only implementation of the browser context.
return static_cast<AwBrowserContext*>(web_contents->GetBrowserContext());
}
base::FilePath AwBrowserContext::GetHttpCachePath() {
return http_cache_path_;
}
base::FilePath AwBrowserContext::GetPrefStorePath() {
return GetPath().Append(FILE_PATH_LITERAL("Preferences"));
}
base::FilePath AwBrowserContext::GetCookieStorePath() {
return GetCookieManager()->GetCookieStorePath();
}
// static
void AwBrowserContext::RegisterPrefs(PrefRegistrySimple* registry) {
safe_browsing::RegisterProfilePrefs(registry);
// Register the Autocomplete Data Retention Policy pref.
// The default value '0' represents the latest Chrome major version on which
// the retention policy ran. By setting it to a low default value, we're
// making sure it runs now (as it only runs once per major version).
registry->RegisterIntegerPref(
autofill::prefs::kAutocompleteLastVersionRetentionPolicy, 0);
// We only use the autocomplete feature of Autofill, which is controlled via
// the manager_delegate. We don't use the rest of Autofill, which is why it is
// hardcoded as disabled here.
registry->RegisterBooleanPref(autofill::prefs::kAutofillProfileEnabled,
false);
registry->RegisterBooleanPref(autofill::prefs::kAutofillCreditCardEnabled,
false);
// This contains a map from a given origin to the client hint headers
// requested to be sent next time that origin is loaded.
registry->RegisterDictionaryPref(prefs::kClientHintsCachedPerOriginMap);
#if BUILDFLAG(ENABLE_MOJO_CDM)
cdm::MediaDrmStorageImpl::RegisterProfilePrefs(registry);
#endif
}
void AwBrowserContext::CreateUserPrefService() {
TRACE_EVENT0("startup", "AwBrowserContext::CreateUserPrefService");
auto pref_registry = base::MakeRefCounted<user_prefs::PrefRegistrySyncable>();
RegisterPrefs(pref_registry.get());
PrefServiceFactory pref_service_factory;
PrefNameSet persistent_prefs;
// Persisted to avoid having to provision MediaDrm every time the
// application tries to play protected content after restart.
persistent_prefs.insert(cdm::prefs::kMediaDrmStorage);
// Persisted to ensure client hints can be sent on next page load.
persistent_prefs.insert(prefs::kClientHintsCachedPerOriginMap);
pref_service_factory.set_user_prefs(base::MakeRefCounted<SegregatedPrefStore>(
base::MakeRefCounted<InMemoryPrefStore>(),
base::MakeRefCounted<JsonPrefStore>(GetPrefStorePath()),
std::move(persistent_prefs)));
policy::URLBlocklistManager::RegisterProfilePrefs(pref_registry.get());
AwBrowserPolicyConnector* browser_policy_connector =
AwBrowserProcess::GetInstance()->browser_policy_connector();
pref_service_factory.set_managed_prefs(
base::MakeRefCounted<policy::ConfigurationPolicyPrefStore>(
browser_policy_connector,
browser_policy_connector->GetPolicyService(),
browser_policy_connector->GetHandlerList(),
policy::POLICY_LEVEL_MANDATORY));
{
// TODO(crbug.com/40268809): We can potentially use
// pref_service_factory.set_async(true) instead of ScopedAllowBlocking in
// order to avoid blocking here or to at least parallelize work in the
// background, but it might require additional cross-thread synchronization.
//
// Note that for the default profile blocking IO is already permitted on the
// UI thread due to being called during Chromium/browser
// initialization. ScopedAllowBlocking is explicitly needed for non-default
// profiles as they are instead created from a calling environment where
// normal threading restrictions apply.
base::ScopedAllowBlocking scoped_allow_blocking;
user_pref_service_ = pref_service_factory.Create(pref_registry);
}
if (IsDefaultBrowserContext()) {
MigrateLocalStatePrefs();
}
user_prefs::UserPrefs::Set(this, user_pref_service_.get());
}
void AwBrowserContext::MigrateLocalStatePrefs() {
PrefService* local_state = AwBrowserProcess::GetInstance()->local_state();
if (!local_state->HasPrefPath(cdm::prefs::kMediaDrmStorage)) {
return;
}
user_pref_service_->Set(cdm::prefs::kMediaDrmStorage,
local_state->GetValue(cdm::prefs::kMediaDrmStorage));
local_state->ClearPref(cdm::prefs::kMediaDrmStorage);
}
// static
std::vector<std::string> AwBrowserContext::GetAuthSchemes() {
// In Chrome this is configurable via the AuthSchemes policy. For WebView
// there is no interest to have it available so far.
std::vector<std::string> supported_schemes = {"basic", "digest", "ntlm",
"negotiate"};
return supported_schemes;
}
void AwBrowserContext::AddVisitedURLs(const std::vector<GURL>& urls) {
DCHECK(visitedlink_writer_);
visitedlink_writer_->AddURLs(urls);
}
AwQuotaManagerBridge* AwBrowserContext::GetQuotaManagerBridge() {
if (!quota_manager_bridge_.get()) {
quota_manager_bridge_ = AwQuotaManagerBridge::Create(this);
}
return quota_manager_bridge_.get();
}
CookieManager* AwBrowserContext::GetCookieManager() {
if (IsDefaultBrowserContext()) {
// For the default context, the CookieManager isn't owned by the context,
// and may be initialized externally.
CHECK(!cookie_manager_);
return CookieManager::GetDefaultInstance();
} else {
// Non-default contexts own their cookie managers
CHECK(cookie_manager_);
return cookie_manager_.get();
}
}
bool AwBrowserContext::IsDefaultBrowserContext() const {
return is_default_;
}
base::FilePath AwBrowserContext::GetPath() const {
return context_storage_path_;
}
bool AwBrowserContext::IsOffTheRecord() {
// Android WebView does not support off the record profile yet.
return false;
}
content::DownloadManagerDelegate*
AwBrowserContext::GetDownloadManagerDelegate() {
if (!GetUserData(kDownloadManagerDelegateKey)) {
SetUserData(kDownloadManagerDelegateKey,
std::make_unique<AwDownloadManagerDelegate>());
}
return static_cast<AwDownloadManagerDelegate*>(
GetUserData(kDownloadManagerDelegateKey));
}
content::BrowserPluginGuestManager* AwBrowserContext::GetGuestManager() {
return NULL;
}
storage::SpecialStoragePolicy* AwBrowserContext::GetSpecialStoragePolicy() {
// Intentionally returning NULL as 'Extensions' and 'Apps' not supported.
return NULL;
}
content::PlatformNotificationService*
AwBrowserContext::GetPlatformNotificationService() {
return nullptr;
}
content::PushMessagingService* AwBrowserContext::GetPushMessagingService() {
// TODO(johnme): Support push messaging in WebView.
return NULL;
}
content::StorageNotificationService*
AwBrowserContext::GetStorageNotificationService() {
return nullptr;
}
content::SSLHostStateDelegate* AwBrowserContext::GetSSLHostStateDelegate() {
if (!ssl_host_state_delegate_.get()) {
ssl_host_state_delegate_ = std::make_unique<AwSSLHostStateDelegate>();
}
return ssl_host_state_delegate_.get();
}
AwPermissionManager* AwBrowserContext::GetPermissionControllerDelegate() {
if (!permission_manager_.get())
permission_manager_ = std::make_unique<AwPermissionManager>(*this);
return permission_manager_.get();
}
content::ClientHintsControllerDelegate*
AwBrowserContext::GetClientHintsControllerDelegate() {
if (!client_hints_controller_delegate_.get()) {
client_hints_controller_delegate_ =
std::make_unique<AwClientHintsControllerDelegate>(GetPrefService());
}
return client_hints_controller_delegate_.get();
}
content::BackgroundFetchDelegate*
AwBrowserContext::GetBackgroundFetchDelegate() {
return nullptr;
}
content::BackgroundSyncController*
AwBrowserContext::GetBackgroundSyncController() {
return nullptr;
}
content::BrowsingDataRemoverDelegate*
AwBrowserContext::GetBrowsingDataRemoverDelegate() {
return nullptr;
}
content::FileSystemAccessPermissionContext*
AwBrowserContext::GetFileSystemAccessPermissionContext() {
return &fsa_permission_context_;
}
content::ReduceAcceptLanguageControllerDelegate*
AwBrowserContext::GetReduceAcceptLanguageControllerDelegate() {
return nullptr;
}
std::unique_ptr<download::InProgressDownloadManager>
AwBrowserContext::RetrieveInProgressDownloadManager() {
return std::make_unique<download::InProgressDownloadManager>(
nullptr, base::FilePath(), nullptr,
base::BindRepeating(&IgnoreOriginSecurityCheck),
base::BindRepeating(&content::DownloadRequestUtils::IsURLSafe),
/*wake_lock_provider_binder*/ base::NullCallback());
}
content::OriginTrialsControllerDelegate*
AwBrowserContext::GetOriginTrialsControllerDelegate() {
return origin_trials_controller_delegate_.get();
}
std::unique_ptr<content::ZoomLevelDelegate>
AwBrowserContext::CreateZoomLevelDelegate(
const base::FilePath& partition_path) {
return nullptr;
}
std::string AwBrowserContext::GetExtraHeadersForUrl(const GURL& url) {
// This method of mapping headers to urls supports the WebView.loadUrl with
// extra headers method, and should only be used to support this flow, but not
// for any other purposes of attaching extra headers to requests.
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!url.is_valid()) {
return std::string();
}
std::map<std::string, std::string>::iterator iter =
extra_headers_for_urls_.find(url.spec());
return iter != extra_headers_for_urls_.end() ? iter->second : std::string();
}
void AwBrowserContext::RebuildTable(
const scoped_refptr<URLEnumerator>& enumerator) {
// Android WebView rebuilds from WebChromeClient.getVisitedHistory. The client
// can change in the lifetime of this WebView and may not yet be set here.
// Therefore this initialization path is not used.
enumerator->OnComplete(true);
}
void AwBrowserContext::BuildVisitedLinkTable(
const scoped_refptr<VisitedLinkEnumerator>& enumerator) {
// Partitioned visited link hashtables are not supported in Android WebView,
// so this initialization path is not used.
enumerator->OnVisitedLinkComplete(true);
}
void AwBrowserContext::SetExtendedReportingAllowed(bool allowed) {
user_pref_service_->SetBoolean(
::prefs::kSafeBrowsingExtendedReportingOptInAllowed, allowed);
}
// TODO(amalova): Make sure NetworkContextParams is configured correctly when
// off-the-record
void AwBrowserContext::ConfigureNetworkContextParams(
bool in_memory,
const base::FilePath& relative_partition_path,
network::mojom::NetworkContextParams* context_params,
cert_verifier::mojom::CertVerifierCreationParams*
cert_verifier_creation_params) {
context_params->user_agent = android_webview::GetUserAgent();
// TODO(ntfschr): set this value to a proper value based on the user's
// preferred locales (http://crbug.com/898555).
context_params->accept_language = GetDefaultAcceptLanguageHeader();
// HTTP cache
context_params->http_cache_enabled = true;
context_params->http_cache_max_size = GetHttpCacheSize();
// WebView should persist and restore cookies between app sessions (including
// session cookies).
context_params->file_paths = network::mojom::NetworkContextFilePaths::New();
// Adding HTTP cache dir here
context_params->file_paths->http_cache_directory = GetHttpCachePath();
base::FilePath cookie_path = AwBrowserContext::GetCookieStorePath();
context_params->file_paths->data_directory = cookie_path.DirName();
context_params->file_paths->cookie_database_name = cookie_path.BaseName();
context_params->restore_old_session_cookies = true;
context_params->persist_session_cookies = true;
context_params->cookie_manager_params =
network::mojom::CookieManagerParams::New();
context_params->cookie_manager_params->allow_file_scheme_cookies =
GetCookieManager()->GetAllowFileSchemeCookies();
context_params->cookie_manager_params->cookie_access_delegate_type =
base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kWebViewEnableModernCookieSameSite)
? network::mojom::CookieAccessDelegateType::ALWAYS_NONLEGACY
: network::mojom::CookieAccessDelegateType::ALWAYS_LEGACY;
context_params->initial_ssl_config = network::mojom::SSLConfig::New();
// Allow SHA-1 to be used for locally-installed trust anchors, as WebView
// should behave like the Android system would.
context_params->initial_ssl_config->sha1_local_anchors_enabled = true;
// WebView supports Certificate Transparency from Android B via Android's CT
// policy. (http://crbug.com/921750).
context_params->enforce_chrome_ct_policy = false;
context_params->enable_brotli = true;
context_params->enable_zstd = true;
context_params->stale_dns_enabled = enable_stale_dns_;
context_params->check_clear_text_permitted =
AwContentBrowserClient::get_check_cleartext_permitted();
if (base::FeatureList::IsEnabled(features::kWebViewQuicConnectionTimeout)) {
context_params->quic_idle_connection_timeout_seconds =
features::kWebViewQuicConnectionTimeoutSeconds.Get();
}
// Add proxy settings
AwProxyConfigMonitor::GetInstance()->AddProxyToNetworkContextParams(
context_params);
}
base::android::ScopedJavaLocalRef<jobject> JNI_AwBrowserContext_GetDefaultJava(
JNIEnv* env) {
AwBrowserContext* default_context = AwBrowserContext::GetDefault();
CHECK(default_context);
return default_context->GetJavaBrowserContext();
}
std::string JNI_AwBrowserContext_GetDefaultContextName(JNIEnv* env) {
return AwBrowserContextStore::kDefaultContextName;
}
std::string JNI_AwBrowserContext_GetDefaultContextRelativePath(JNIEnv* env) {
return AwBrowserContextStore::kDefaultContextPath;
}
void AwBrowserContext::ClearPersistentOriginTrialStorageForTesting(
JNIEnv* env) {
content::OriginTrialsControllerDelegate* delegate =
GetOriginTrialsControllerDelegate();
if (delegate)
delegate->ClearPersistedTokens();
}
base::android::ScopedJavaLocalRef<jobject>
AwBrowserContext::GetJavaBrowserContext() {
if (!obj_) {
JNIEnv* env = base::android::AttachCurrentThread();
obj_ = Java_AwBrowserContext_create(
env, reinterpret_cast<intptr_t>(this), name_, relative_path_.value(),
GetCookieManager()->GetJavaCookieManager(),
prefetch_manager_->GetJavaPrefetchManager(),
preconnector_->GetJavaAwPreconnector(), IsDefaultBrowserContext());
}
return base::android::ScopedJavaLocalRef<jobject>(obj_);
}
jlong AwBrowserContext::GetQuotaManagerBridge(JNIEnv* env) {
return reinterpret_cast<intptr_t>(GetQuotaManagerBridge());
}
void AwBrowserContext::SetExtraHeadersForUrl(const GURL& url,
const std::string& headers) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!url.is_valid()) {
return;
}
if (!headers.empty()) {
extra_headers_for_urls_[url.spec()] = headers;
} else {
extra_headers_for_urls_.erase(url.spec());
}
}
// static
jboolean JNI_AwBrowserContext_IsValidHttpHeaderName(JNIEnv* env,
std::string& header_name) {
return net::HttpUtil::IsValidHeaderName(header_name);
}
// static
jboolean JNI_AwBrowserContext_IsValidHttpHeaderValue(
JNIEnv* env,
std::string& header_value) {
return net::HttpUtil::IsValidHeaderValue(header_value);
}
std::vector<std::string> AwBrowserContext::SetOriginMatchedHeader(
JNIEnv* env,
std::string& header_name,
std::string& header_value,
const std::vector<std::string>& rules) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
origin_matcher::OriginMatcher matcher;
std::vector<std::string> rejected;
for (const std::string& rule : rules) {
if (!matcher.AddRuleFromString(rule)) {
rejected.emplace_back(rule);
}
}
if (!rejected.empty()) {
return rejected;
}
// We only maintain a single mapping for each header name by design.
auto it = std::ranges::find_if(
origin_matched_headers_,
AwOriginMatchedHeader::LookupPredicate(header_name,
/*value=*/std::nullopt));
if (it == origin_matched_headers_.end()) {
origin_matched_headers_.emplace_back(
base::MakeRefCounted<AwOriginMatchedHeader>(std::move(header_name),
std::move(header_value),
std::move(matcher)));
} else {
*it = base::MakeRefCounted<AwOriginMatchedHeader>(
std::move(header_name), std::move(header_value), std::move(matcher));
}
return {};
}
std::vector<std::string> AwBrowserContext::AddOriginMatchedHeader(
JNIEnv* env,
std::string& header_name,
std::string& header_value,
const std::vector<std::string>& rules) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
origin_matcher::OriginMatcher matcher;
std::vector<std::string> rejected;
for (const std::string& rule : rules) {
if (!matcher.AddRuleFromString(rule)) {
rejected.emplace_back(rule);
}
}
if (!rejected.empty()) {
return rejected;
}
auto it = std::ranges::find_if(
origin_matched_headers_,
AwOriginMatchedHeader::LookupPredicate(header_name, header_value));
if (it == origin_matched_headers_.end()) {
origin_matched_headers_.emplace_back(
base::MakeRefCounted<AwOriginMatchedHeader>(std::move(header_name),
std::move(header_value),
std::move(matcher)));
} else {
*it = (*it)->MergedWithMatcher(std::move(matcher));
}
return {};
}
bool AwBrowserContext::HasOriginMatchedHeader(JNIEnv* env,
const std::string& header_name) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
return std::ranges::find_if(origin_matched_headers_,
AwOriginMatchedHeader::LookupPredicate(
header_name, /*value=*/std::nullopt)) !=
origin_matched_headers_.end();
}
std::vector<scoped_refptr<AwOriginMatchedHeader>>
AwBrowserContext::FindOriginMatchedHeaders(
JNIEnv* env,
const std::optional<std::string>& header_name,
const std::optional<std::string>& header_value) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!header_name) {
return origin_matched_headers_;
}
std::vector<scoped_refptr<AwOriginMatchedHeader>> matches;
std::ranges::copy_if(
origin_matched_headers_, std::back_inserter(matches),
AwOriginMatchedHeader::LookupPredicate(*header_name, header_value));
return matches;
}
void AwBrowserContext::ClearOriginMatchedHeader(
JNIEnv* env,
const std::string& header_name,
const std::optional<std::string>& header_value) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
std::erase_if(origin_matched_headers_, AwOriginMatchedHeader::LookupPredicate(
header_name, header_value));
}
void AwBrowserContext::ClearAllOriginMatchedHeaders(JNIEnv* env) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
origin_matched_headers_.clear();
}
const std::vector<scoped_refptr<AwOriginMatchedHeader>>&
AwBrowserContext::GetOriginMatchedHeaders() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
return origin_matched_headers_;
}
void AwBrowserContext::AddQuicHints(JNIEnv* env,
const std::vector<GURL>& origins) {
std::vector<url::SchemeHostPort> scheme_host_ports(origins.size());
for (const GURL& origin : origins) {
scheme_host_ports.emplace_back(origin);
}
GetDefaultStoragePartition()->GetNetworkContext()->AddQuicHints(
scheme_host_ports, net::NetworkAnonymizationKey());
}
void AwBrowserContext::SetServiceWorkerIoThreadClient(
JNIEnv* const env,
const base::android::JavaParamRef<jobject>& io_thread_client) {
sw_io_thread_client_ =
base::android::ScopedJavaGlobalRef<jobject>(io_thread_client);
}
int AwBrowserContext::AllowedPrerenderingCount() const {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
return allowed_prerendering_count_;
}
void AwBrowserContext::SetAllowedPrerenderingCount(JNIEnv* const env,
int allowed_count) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
CHECK_GT(allowed_count, 0);
allowed_prerendering_count_ =
std::min(allowed_count, MAX_ALLOWED_PRERENDERING_COUNT);
}
void AwBrowserContext::WarmUpSpareRenderer(JNIEnv* const env) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
base::TimeTicks start_time = base::TimeTicks::Now();
content::RenderProcessHost* rph =
content::SpareRenderProcessHostManager::Get().WarmupSpare(this);
base::UmaHistogramTimes("Android.WebView.WarmUpSpareRenderer.Duration",
base::TimeTicks::Now() - start_time);
base::UmaHistogramBoolean(
"Android.WebView.WarmUpSpareRenderer.StartsNewRenderer", rph != nullptr);
}
std::unique_ptr<AwContentsIoThreadClient>
AwBrowserContext::GetServiceWorkerIoThreadClientThreadSafe() {
base::android::ScopedJavaLocalRef<jobject> java_delegate =
base::android::ScopedJavaLocalRef<jobject>(sw_io_thread_client_);
if (java_delegate) {
return std::make_unique<AwContentsIoThreadClient>(java_delegate);
}
return nullptr;
}
// static
base::FilePath AwBrowserContext::BuildStoragePath(
const base::FilePath& relative_path) {
base::FilePath user_data_dir;
if (!base::PathService::Get(base::DIR_ANDROID_APP_DATA, &user_data_dir)) {
NOTREACHED() << "Failed to get app data directory for Android WebView";
}
return user_data_dir.Append(relative_path);
}
// static
void AwBrowserContext::PrepareNewContext(const base::FilePath& relative_path) {
base::ScopedAllowBlocking scoped_allow_blocking;
const base::FilePath storage_path = BuildStoragePath(relative_path);
bool storage_created = base::CreateDirectory(storage_path);
CHECK(storage_created);
}
// static
void AwBrowserContext::DeleteContext(const base::FilePath& relative_path) {
// The default profile handles its own directory creation in migration code
// and (as of writing) should never be deleted.
CHECK_NE(relative_path.value(), AwBrowserContextStore::kDefaultContextPath);
// TODO(crbug.com/40268809): This could be partially backgrounded by deleting
// on the thread pool. Ideally, any interrupted profile directory deletion
// would be resumed in the background on startup. For now, this just deletes
// synchronously.
//
// We probably also won't want to CHECK in the final solution, but perhaps
// instead allow for some kind of retry-later logic.
base::ScopedAllowBlocking scoped_allow_blocking;
const base::FilePath storage_path = BuildStoragePath(relative_path);
const base::FilePath cache_path = BuildCachePath(relative_path);
bool storage_deleted = base::DeletePathRecursively(storage_path);
CHECK(storage_deleted);
bool cache_deleted = base::DeletePathRecursively(cache_path);
CHECK(cache_deleted);
JNIEnv* env = base::android::AttachCurrentThread();
Java_AwBrowserContext_deleteSharedPreferences(env, relative_path.value());
}
blink::mojom::PermissionStatus AwBrowserContext::GetGeolocationPermission(
const GURL& origin) const {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
JNIEnv* env = base::android::AttachCurrentThread();
if (!obj_) {
return blink::mojom::PermissionStatus::ASK;
}
return static_cast<blink::mojom::PermissionStatus>(
Java_AwBrowserContext_getGeolocationPermission(env, obj_, origin.spec()));
}
std::string AwBrowserContext::GetDefaultAcceptLanguageHeader() {
// For now, set this to "en-US,en" instead of "en-us,en", since Android
// guarantees region codes will be uppercase.
return net::HttpUtil::GenerateAcceptLanguageHeader("en-US,en");
}
mojo::PendingRemote<network::mojom::URLLoaderFactory>
AwBrowserContext::CreateURLLoaderFactory() {
auto url_loader_factory_params =
network::mojom::URLLoaderFactoryParams::New();
url_loader_factory_params->process_id = network::mojom::kBrowserProcessId;
url_loader_factory_params->is_orb_enabled = false;
mojo::PendingRemote<network::mojom::URLLoaderFactory> factory;
GetDefaultStoragePartition()->GetNetworkContext()->CreateURLLoaderFactory(
factory.InitWithNewPipeAndPassReceiver(),
std::move(url_loader_factory_params));
return factory;
}
} // namespace android_webview