chromium/android_webview/browser/aw_settings.cc

// Copyright 2013 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_settings.h"

#include <memory>

#include "android_webview/browser/aw_browser_context.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.h"
#include "android_webview/browser/aw_contents_origin_matcher.h"
#include "android_webview/browser/aw_dark_mode.h"
#include "android_webview/browser/aw_user_agent_metadata.h"
#include "android_webview/browser/metrics/aw_metrics_service_accessor.h"
#include "android_webview/browser/metrics/aw_metrics_service_client.h"
#include "android_webview/browser/renderer_host/aw_render_view_host_ext.h"
#include "android_webview/common/aw_content_client.h"
#include "android_webview/common/aw_features.h"
#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/containers/contains.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/supports_user_data.h"
#include "components/embedder_support/user_agent_utils.h"
#include "components/viz/common/features.h"
#include "content/public/browser/back_forward_cache.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/renderer_preferences_util.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "net/http/http_util.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/renderer_preferences/renderer_preferences.h"
#include "third_party/blink/public/mojom/webpreferences/web_preferences.mojom.h"
#include "url/gurl.h"
#include "url/origin.h"

// Must come after all headers that specialize FromJniType() / ToJniType().
#include "android_webview/browser_jni_headers/AwSettings_jni.h"

using base::android::ConvertJavaStringToUTF16;
using base::android::ConvertJavaStringToUTF8;
using base::android::ConvertUTF8ToJavaString;
using base::android::JavaParamRef;
using base::android::ScopedJavaLocalRef;
using blink::web_pref::WebPreferences;

namespace android_webview {

namespace {

// Metrics on the count of difference cases when we populate the user-agent
// metadata. These values are persisted to logs. Entries should not be
// renumbered and numeric values should never be reused.
enum class UserAgentMetadataAvailableType {
  kSystemDefault = 0,
  kSystemDefaultLowEntropyOnly = 1,
  kUserOverrides = 2,
  kMaxValue = kUserOverrides,
};

void LogUserAgentMetadataAvailableType(UserAgentMetadataAvailableType type) {
  base::UmaHistogramEnumeration(
      "Android.WebView.UserAgentClientHintsMetadata.AvailableType", type);
}

void PopulateFixedWebPreferences(WebPreferences* web_prefs) {
  web_prefs->shrinks_standalone_images_to_fit = false;
  web_prefs->should_clear_document_background = false;
  web_prefs->viewport_meta_enabled = true;
  web_prefs->picture_in_picture_enabled = false;
  web_prefs->disable_accelerated_small_canvases = true;
  // WebView has historically not adjusted font scale for text autosizing.
  web_prefs->device_scale_adjustment = 1.0;
}

const void* const kAwSettingsUserDataKey = &kAwSettingsUserDataKey;

}  // namespace

class AwSettingsUserData : public base::SupportsUserData::Data {
 public:
  explicit AwSettingsUserData(AwSettings* ptr) : settings_(ptr) {}

  static AwSettings* GetSettings(content::WebContents* web_contents) {
    if (!web_contents)
      return NULL;
    AwSettingsUserData* data = static_cast<AwSettingsUserData*>(
        web_contents->GetUserData(kAwSettingsUserDataKey));
    return data ? data->settings_.get() : NULL;
  }

 private:
  raw_ptr<AwSettings> settings_;
};

AwSettings::AwSettings(JNIEnv* env,
                       const jni_zero::JavaRef<jobject>& obj,
                       content::WebContents* web_contents)
    : WebContentsObserver(web_contents),
      xrw_allowlist_matcher_(base::MakeRefCounted<AwContentsOriginMatcher>()),
      aw_settings_(env, obj) {
  web_contents->SetUserData(kAwSettingsUserDataKey,
                            std::make_unique<AwSettingsUserData>(this));
}

AwSettings::~AwSettings() {
  if (web_contents()) {
    web_contents()->SetUserData(kAwSettingsUserDataKey, NULL);
  }

  JNIEnv* env = base::android::AttachCurrentThread();
  ScopedJavaLocalRef<jobject> scoped_obj = aw_settings_.get(env);
  if (!scoped_obj)
    return;
  Java_AwSettings_nativeAwSettingsGone(env, scoped_obj,
                                       reinterpret_cast<intptr_t>(this));
}

bool AwSettings::GetJavaScriptCanOpenWindowsAutomatically() {
  return javascript_can_open_windows_automatically_;
}

bool AwSettings::GetAllowThirdPartyCookies() {
  return allow_third_party_cookies_;
}

bool AwSettings::GetJavaScriptEnabled() {
  return javascript_enabled_;
}

AwSettings::MixedContentMode AwSettings::GetMixedContentMode() {
  return mixed_content_mode_;
}

AwSettings::AttributionBehavior AwSettings::GetAttributionBehavior() {
  return attribution_behavior_;
}

bool AwSettings::IsPrerender2Allowed() {
  return (speculative_loading_allowed_flags_ & PRERENDER_ENABLED);
}

bool AwSettings::IsBackForwardCacheEnabled() {
  return bfcache_enabled_in_java_settings_;
}

void AwSettings::Destroy(JNIEnv* env, const JavaParamRef<jobject>& obj) {
  delete this;
}

AwSettings* AwSettings::FromWebContents(content::WebContents* web_contents) {
  return AwSettingsUserData::GetSettings(web_contents);
}

bool AwSettings::GetAllowSniffingFileUrls() {
  JNIEnv* env = base::android::AttachCurrentThread();
  return Java_AwSettings_getAllowSniffingFileUrls(env);
}

AwSettings::RequestedWithHeaderMode
AwSettings::GetDefaultRequestedWithHeaderMode() {
  // If the control feature is not enabled, the default is the old behavior,
  // which is to send the app package name.
  if (!base::FeatureList::IsEnabled(
          features::kWebViewXRequestedWithHeaderControl))
    return AwSettings::RequestedWithHeaderMode::APP_PACKAGE_NAME;

  int configuredValue = features::kWebViewXRequestedWithHeaderMode.Get();
  switch (configuredValue) {
    case AwSettings::RequestedWithHeaderMode::CONSTANT_WEBVIEW:
      return AwSettings::RequestedWithHeaderMode::CONSTANT_WEBVIEW;
    case AwSettings::RequestedWithHeaderMode::NO_HEADER:
      return AwSettings::RequestedWithHeaderMode::NO_HEADER;
    default:
      // If the field trial config is broken for some reason, use the
      // package name.
      return AwSettings::RequestedWithHeaderMode::APP_PACKAGE_NAME;
  }
}

AwRenderViewHostExt* AwSettings::GetAwRenderViewHostExt() {
  if (!web_contents())
    return NULL;
  AwContents* contents = AwContents::FromWebContents(web_contents());
  if (!contents)
    return NULL;
  return contents->render_view_host_ext();
}

void AwSettings::ResetScrollAndScaleState(JNIEnv* env,
                                          const JavaParamRef<jobject>& obj) {
  AwRenderViewHostExt* rvhe = GetAwRenderViewHostExt();
  if (!rvhe)
    return;
  rvhe->ResetScrollAndScaleState();
}

void AwSettings::UpdateEverything() {
  JNIEnv* env = base::android::AttachCurrentThread();
  CHECK(env);
  ScopedJavaLocalRef<jobject> scoped_obj = aw_settings_.get(env);
  if (!scoped_obj)
    return;
  // Grab the lock and call UpdateEverythingLocked.
  Java_AwSettings_updateEverything(env, scoped_obj);
}

void AwSettings::UpdateEverythingLocked(JNIEnv* env,
                                        const JavaParamRef<jobject>& obj) {
  base::AutoReset<bool> auto_reset(&in_update_everything_locked_, true);
  UpdateInitialPageScaleLocked(env, obj);
  UpdateWebkitPreferencesLocked(env, obj);
  UpdateUserAgentLocked(env, obj);
  ResetScrollAndScaleState(env, obj);
  UpdateRendererPreferencesLocked(env, obj);
  UpdateOffscreenPreRasterLocked(env, obj);
  UpdateWillSuppressErrorStateLocked(env, obj);
  UpdateCookiePolicyLocked(env, obj);
  UpdateJavaScriptPolicyLocked(env, obj);
  UpdateAllowFileAccessLocked(env, obj);
  UpdateMixedContentModeLocked(env, obj);
  UpdateAttributionBehaviorLocked(env, obj);
  UpdateSpeculativeLoadingAllowedLocked(env, obj);
  UpdateBackForwardCacheEnabledLocked(env, obj);
  UpdateGeolocationEnabledLocked(env, obj);
}

void AwSettings::UpdateUserAgentLocked(JNIEnv* env,
                                       const JavaParamRef<jobject>& obj) {
  if (!web_contents())
    return;

  ScopedJavaLocalRef<jstring> str =
      Java_AwSettings_getUserAgentLocked(env, obj);
  bool ua_overidden = !!str;
  bool ua_metadata_overridden =
      Java_AwSettings_getHasUserAgentMetadataOverridesLocked(env, obj);

  if (ua_overidden) {
    std::string ua_string_override = ConvertJavaStringToUTF8(str);
    std::string ua_default = GetUserAgent();
    blink::UserAgentOverride override_ua_with_metadata;
    override_ua_with_metadata.ua_string_override = ua_string_override;

    // If kUACHOverrideBlank is enabled, set user-agent metadata with the
    // default blank value.
    if (!ua_string_override.empty() &&
        base::FeatureList::IsEnabled(blink::features::kUACHOverrideBlank)) {
      override_ua_with_metadata.ua_metadata_override =
          blink::UserAgentMetadata();
    }

    // Generate user-agent client hints in the following three cases:
    // 1. If user provide the user-agent metadata overrides, we use the
    // override data to populate the user-agent client hints.
    // 2. Otherwise, if override user-agent contains default user-agent, we
    // use system default user-agent metadata to populate the user-agent
    // client hints.
    // 3. Finally, if the above two cases don't match, we only populate system
    // default low-entropy client hints.
    if (ua_metadata_overridden) {
      ScopedJavaLocalRef<jobject> java_ua_metadata =
          Java_AwSettings_getUserAgentMetadataLocked(env, obj);
      override_ua_with_metadata.ua_metadata_override =
          FromJavaAwUserAgentMetadata(env, java_ua_metadata);
      LogUserAgentMetadataAvailableType(
          UserAgentMetadataAvailableType::kUserOverrides);
    } else if (base::Contains(ua_string_override, ua_default)) {
      override_ua_with_metadata.ua_metadata_override =
          AwClientHintsControllerDelegate::GetUserAgentMetadataOverrideBrand();
      LogUserAgentMetadataAvailableType(
          UserAgentMetadataAvailableType::kSystemDefault);
    } else {
      override_ua_with_metadata.ua_metadata_override =
          AwClientHintsControllerDelegate::GetUserAgentMetadataOverrideBrand(
              /*only_low_entropy_ch=*/true);
      LogUserAgentMetadataAvailableType(
          UserAgentMetadataAvailableType::kSystemDefaultLowEntropyOnly);
    }

    // Set overridden user-agent and default client hints metadata if applied.
    web_contents()->SetUserAgentOverride(override_ua_with_metadata, true);
  }

  content::NavigationController& controller = web_contents()->GetController();
  for (int i = 0; i < controller.GetEntryCount(); ++i)
    controller.GetEntryAtIndex(i)->SetIsOverridingUserAgent(ua_overidden);
}

void AwSettings::UpdateWebkitPreferencesLocked(
    JNIEnv* env,
    const JavaParamRef<jobject>& obj) {
  if (!web_contents())
    return;
  AwRenderViewHostExt* render_view_host_ext = GetAwRenderViewHostExt();
  if (!render_view_host_ext)
    return;

  web_contents()->OnWebPreferencesChanged();
}

void AwSettings::UpdateInitialPageScaleLocked(
    JNIEnv* env,
    const JavaParamRef<jobject>& obj) {
  AwRenderViewHostExt* rvhe = GetAwRenderViewHostExt();
  if (!rvhe)
    return;

  float initial_page_scale_percent =
      Java_AwSettings_getInitialPageScalePercentLocked(env, obj);
  if (initial_page_scale_percent == 0) {
    rvhe->SetInitialPageScale(-1);
  } else {
    float dip_scale =
        static_cast<float>(Java_AwSettings_getDIPScaleLocked(env, obj));
    rvhe->SetInitialPageScale(initial_page_scale_percent / dip_scale / 100.0f);
    initial_page_scale_is_non_default_ = true;
  }
}

void AwSettings::UpdateWillSuppressErrorStateLocked(
    JNIEnv* env,
    const JavaParamRef<jobject>& obj) {
  AwRenderViewHostExt* rvhe = GetAwRenderViewHostExt();
  if (!rvhe)
    return;

  bool suppress = Java_AwSettings_getWillSuppressErrorPageLocked(env, obj);
  rvhe->SetWillSuppressErrorPage(suppress);
}

void AwSettings::UpdateRendererPreferencesLocked(
    JNIEnv* env,
    const JavaParamRef<jobject>& obj) {
  if (!web_contents())
    return;

  bool update_prefs = false;
  blink::RendererPreferences* prefs = web_contents()->GetMutableRendererPrefs();

  if (!renderer_prefs_initialized_) {
    content::UpdateFontRendererPreferencesFromSystemSettings(prefs);
    renderer_prefs_initialized_ = true;
    update_prefs = true;
  }

  if (prefs->accept_languages.compare(
          AwContentBrowserClient::GetAcceptLangsImpl())) {
    prefs->accept_languages = AwContentBrowserClient::GetAcceptLangsImpl();
    update_prefs = true;
  }

  if (update_prefs)
    web_contents()->SyncRendererPrefs();

  if (update_prefs) {
    // make sure to update accept languages when the network service is enabled
    AwBrowserContext* aw_browser_context =
        AwBrowserContext::FromWebContents(web_contents());
    // AndroidWebview does not use per-site storage partitions.
    content::StoragePartition* storage_partition =
        aw_browser_context->GetDefaultStoragePartition();
    std::string expanded_language_list =
        net::HttpUtil::ExpandLanguageList(prefs->accept_languages);
    storage_partition->GetNetworkContext()->SetAcceptLanguage(
        net::HttpUtil::GenerateAcceptLanguageHeader(expanded_language_list));
  }
}

void AwSettings::UpdateCookiePolicyLocked(JNIEnv* env,
                                          const JavaParamRef<jobject>& obj) {
  if (!web_contents())
    return;

  allow_third_party_cookies_ =
      Java_AwSettings_getAcceptThirdPartyCookiesLocked(env, obj);
}

void AwSettings::UpdateJavaScriptPolicyLocked(
    JNIEnv* env,
    const JavaParamRef<jobject>& obj) {
  if (!web_contents())
    return;

  javascript_enabled_ = Java_AwSettings_getJavaScriptEnabledLocked(env, obj);
}

void AwSettings::UpdateOffscreenPreRasterLocked(
    JNIEnv* env,
    const JavaParamRef<jobject>& obj) {
  AwContents* contents = AwContents::FromWebContents(web_contents());
  if (contents) {
    contents->SetOffscreenPreRaster(
        Java_AwSettings_getOffscreenPreRasterLocked(env, obj));
  }
}

void AwSettings::UpdateAllowFileAccessLocked(JNIEnv* env,
                                             const JavaParamRef<jobject>& obj) {
  if (!web_contents())
    return;

  allow_file_access_ = Java_AwSettings_getAllowFileAccess(env, obj);
}

void AwSettings::UpdateMixedContentModeLocked(
    JNIEnv* env,
    const JavaParamRef<jobject>& obj) {
  if (!web_contents())
    return;

  mixed_content_mode_ = static_cast<MixedContentMode>(
      Java_AwSettings_getMixedContentMode(env, obj));
}

void AwSettings::UpdateAttributionBehaviorLocked(
    JNIEnv* env,
    const JavaParamRef<jobject>& obj) {
  if (!web_contents()) {
    return;
  }

  AttributionBehavior previous = attribution_behavior_;
  attribution_behavior_ = static_cast<AttributionBehavior>(
      Java_AwSettings_getAttributionBehavior(env, obj));

  base::UmaHistogramEnumeration("Conversions.AttributionBehavior",
                                attribution_behavior_);

  // If attribution was previously disabled or has now been disabled, then
  // we need to update attribution support values in the renderer.
  if (previous != attribution_behavior_ &&
      (previous == AwSettings::AttributionBehavior::DISABLED ||
       attribution_behavior_ == AwSettings::AttributionBehavior::DISABLED)) {
    web_contents()->UpdateAttributionSupportRenderer();
  }
}

void AwSettings::UpdateSpeculativeLoadingAllowedLocked(
    JNIEnv* env,
    const JavaParamRef<jobject>& obj) {
  SpeculativeLoadingAllowedFlags previous = speculative_loading_allowed_flags_;
  speculative_loading_allowed_flags_ =
      static_cast<SpeculativeLoadingAllowedFlags>(
          Java_AwSettings_getSpeculativeLoadingAllowed(env, obj));

  if (!in_update_everything_locked_) {
    // The setting was explicitly updated, since this is not part of the
    // UpdateEverythingLocked call. Register a synthetic field trial so that
    // even if we do experiments that are not run via Finch, we can still
    // identify the "Prerender enabled" vs "Prerender disabled" groups for UMA
    // and crash comparison. Note that we only register when the setting was
    // explicitly updated, to exclude cases that are not part of any experiment
    // groups at all (e.g. when we're on a version of the WebView embedder that
    // doesn't have the experiment at all).
    static constexpr char kPrerenderTrial[] = "WebViewPrerenderSynthetic";
    AwMetricsServiceAccessor::RegisterSyntheticFieldTrial(
        AwMetricsServiceClient::GetInstance()->GetMetricsService(),
        kPrerenderTrial,
        (speculative_loading_allowed_flags_ & AwSettings::PRERENDER_ENABLED)
            ? "Enabled"
            : "Disabled",
        variations::SyntheticTrialAnnotationMode::kNextLog);
  }

  if (previous == speculative_loading_allowed_flags_) {
    return;
  }

  if (!web_contents()) {
    // No need to cancel preloading entries if the WebContents that host them
    // doesn't exist.
    return;
  }

  // TODO(crbug.com/339561855): Clear navigational prefetches when
  // preloading is disabled.

  if ((previous & AwSettings::PRERENDER_ENABLED) &&
      !(speculative_loading_allowed_flags_ & AwSettings::PRERENDER_ENABLED)) {
    web_contents()->CancelAllPrerendering();
  }
}

void AwSettings::UpdateBackForwardCacheEnabledLocked(
    JNIEnv* env,
    const JavaParamRef<jobject>& obj) {
  bool bfcache_enabled_by_feature_flag =
      base::FeatureList::IsEnabled(features::kWebViewBackForwardCache);
  bool previous_enabled =
      bfcache_enabled_in_java_settings_ || bfcache_enabled_by_feature_flag;
  bfcache_enabled_in_java_settings_ =
      Java_AwSettings_getBackForwardCacheEnabled(env, obj);
  bool current_enabled =
      bfcache_enabled_in_java_settings_ || bfcache_enabled_by_feature_flag;

  if (!current_enabled && previous_enabled && web_contents()) {
    AwContents* contents = AwContents::FromWebContents(web_contents());
    contents->FlushBackForwardCache(
        env, static_cast<int>(content::BackForwardCache::NotRestoredReason::
                                  kWebViewSettingsChanged));
  }

  if (!in_update_everything_locked_) {
    // The setting was explicitly updated, since this is not part of the
    // UpdateEverythingLocked call. Register a synthetic field trial so that
    // even if we do experiments that are not run via Finch, we can still
    // identify the "BFCache enabled" vs "BFCache disabled" groups for UMA and
    // crash comparison. Note that we only register when the setting was
    // explicitly updated, to exclude cases that are not part of any experiment
    // groups at all (e.g. when we're on a version of the WebView embedder that
    // doesn't have the experiment at all).
    static constexpr char kBackForwardCacheTrial[] =
        "WebViewBackForwardCacheSynthetic";
    AwMetricsServiceAccessor::RegisterSyntheticFieldTrial(
        AwMetricsServiceClient::GetInstance()->GetMetricsService(),
        kBackForwardCacheTrial,
        bfcache_enabled_in_java_settings_ ? "Enabled" : "Disabled",
        variations::SyntheticTrialAnnotationMode::kNextLog);
  }
}

void AwSettings::UpdateGeolocationEnabledLocked(
    JNIEnv* env,
    const JavaParamRef<jobject>& obj) {
  if (!web_contents()) {
    return;
  }

  geolocation_enabled_ = Java_AwSettings_getGeolocationEnabled(env, obj);
}

void AwSettings::RenderViewHostChanged(content::RenderViewHost* old_host,
                                       content::RenderViewHost* new_host) {
  DCHECK_EQ(new_host, web_contents()->GetRenderViewHost());

  UpdateEverything();
}

void AwSettings::WebContentsDestroyed() {
  delete this;
}

void AwSettings::PopulateWebPreferences(WebPreferences* web_prefs) {
  JNIEnv* env = base::android::AttachCurrentThread();
  CHECK(env);
  ScopedJavaLocalRef<jobject> scoped_obj = aw_settings_.get(env);
  if (!scoped_obj)
    return;
  // Grab the lock and call PopulateWebPreferencesLocked.
  Java_AwSettings_populateWebPreferences(env, scoped_obj,
                                         reinterpret_cast<jlong>(web_prefs));
}

void AwSettings::PopulateWebPreferencesLocked(JNIEnv* env,
                                              const JavaParamRef<jobject>& obj,
                                              jlong web_prefs_ptr) {
  AwRenderViewHostExt* render_view_host_ext = GetAwRenderViewHostExt();
  if (!render_view_host_ext)
    return;

  WebPreferences* web_prefs = reinterpret_cast<WebPreferences*>(web_prefs_ptr);
  PopulateFixedWebPreferences(web_prefs);

  web_prefs->text_autosizing_enabled =
      Java_AwSettings_getTextAutosizingEnabledLocked(env, obj);

  int text_size_percent = Java_AwSettings_getTextSizePercentLocked(env, obj);
  if (web_prefs->text_autosizing_enabled) {
    web_prefs->font_scale_factor = text_size_percent / 100.0f;
    web_prefs->force_enable_zoom = text_size_percent >= 130;
    // Use the default zoom factor value when Text Autosizer is turned on.
    render_view_host_ext->SetTextZoomFactor(1);
  } else {
    web_prefs->force_enable_zoom = false;
    render_view_host_ext->SetTextZoomFactor(text_size_percent / 100.0f);
  }

  web_prefs->standard_font_family_map[blink::web_pref::kCommonScript] =
      ConvertJavaStringToUTF16(
          Java_AwSettings_getStandardFontFamilyLocked(env, obj));

  web_prefs->fixed_font_family_map[blink::web_pref::kCommonScript] =
      ConvertJavaStringToUTF16(
          Java_AwSettings_getFixedFontFamilyLocked(env, obj));

  web_prefs->sans_serif_font_family_map[blink::web_pref::kCommonScript] =
      ConvertJavaStringToUTF16(
          Java_AwSettings_getSansSerifFontFamilyLocked(env, obj));

  web_prefs->serif_font_family_map[blink::web_pref::kCommonScript] =
      ConvertJavaStringToUTF16(
          Java_AwSettings_getSerifFontFamilyLocked(env, obj));

  web_prefs->cursive_font_family_map[blink::web_pref::kCommonScript] =
      ConvertJavaStringToUTF16(
          Java_AwSettings_getCursiveFontFamilyLocked(env, obj));

  web_prefs->fantasy_font_family_map[blink::web_pref::kCommonScript] =
      ConvertJavaStringToUTF16(
          Java_AwSettings_getFantasyFontFamilyLocked(env, obj));

  web_prefs->default_encoding = ConvertJavaStringToUTF8(
      Java_AwSettings_getDefaultTextEncodingLocked(env, obj));

  web_prefs->minimum_font_size =
      Java_AwSettings_getMinimumFontSizeLocked(env, obj);

  web_prefs->minimum_logical_font_size =
      Java_AwSettings_getMinimumLogicalFontSizeLocked(env, obj);

  web_prefs->default_font_size =
      Java_AwSettings_getDefaultFontSizeLocked(env, obj);

  web_prefs->default_fixed_font_size =
      Java_AwSettings_getDefaultFixedFontSizeLocked(env, obj);

  // Blink's LoadsImagesAutomatically and ImagesEnabled must be
  // set cris-cross to Android's. See
  // https://code.google.com/p/chromium/issues/detail?id=224317#c26
  web_prefs->loads_images_automatically =
      Java_AwSettings_getImagesEnabledLocked(env, obj);
  web_prefs->images_enabled =
      Java_AwSettings_getLoadsImagesAutomaticallyLocked(env, obj);

  web_prefs->javascript_enabled =
      Java_AwSettings_getJavaScriptEnabledLocked(env, obj);

  web_prefs->allow_universal_access_from_file_urls =
      Java_AwSettings_getAllowUniversalAccessFromFileURLsLocked(env, obj);

  allow_file_access_from_file_urls_ =
      Java_AwSettings_getAllowFileAccessFromFileURLsLocked(env, obj);
  web_prefs->allow_file_access_from_file_urls =
      allow_file_access_from_file_urls_;

  javascript_can_open_windows_automatically_ =
      Java_AwSettings_getJavaScriptCanOpenWindowsAutomaticallyLocked(env, obj);

  web_prefs->supports_multiple_windows =
      Java_AwSettings_getSupportMultipleWindowsLocked(env, obj);

  web_prefs->plugins_enabled = false;

  web_prefs->local_storage_enabled =
      Java_AwSettings_getDomStorageEnabledLocked(env, obj);

  web_prefs->databases_enabled =
      Java_AwSettings_getDatabaseEnabledLocked(env, obj);

  web_prefs->wide_viewport_quirk = true;
  web_prefs->use_wide_viewport =
      Java_AwSettings_getUseWideViewportLocked(env, obj);

  web_prefs->force_zero_layout_height =
      Java_AwSettings_getForceZeroLayoutHeightLocked(env, obj);

  const bool zero_layout_height_disables_viewport_quirk =
      Java_AwSettings_getZeroLayoutHeightDisablesViewportQuirkLocked(env, obj);
  web_prefs->viewport_enabled = !(zero_layout_height_disables_viewport_quirk &&
                                  web_prefs->force_zero_layout_height);

  web_prefs->double_tap_to_zoom_enabled =
      Java_AwSettings_supportsDoubleTapZoomLocked(env, obj);

  web_prefs->initialize_at_minimum_page_scale =
      Java_AwSettings_getLoadWithOverviewModeLocked(env, obj);

  initial_page_scale_is_non_default_ |=
      (web_prefs->initialize_at_minimum_page_scale);

  web_prefs->autoplay_policy =
      Java_AwSettings_getMediaPlaybackRequiresUserGestureLocked(env, obj)
          ? blink::mojom::AutoplayPolicy::kUserGestureRequired
          : blink::mojom::AutoplayPolicy::kNoUserGestureRequired;

  ScopedJavaLocalRef<jstring> url =
      Java_AwSettings_getDefaultVideoPosterURLLocked(env, obj);
  web_prefs->default_video_poster_url =
      url.obj() ? GURL(ConvertJavaStringToUTF8(url)) : GURL();

  bool support_quirks = Java_AwSettings_getSupportLegacyQuirksLocked(env, obj);
  // Please see the corresponding Blink settings for bug references.
  web_prefs->support_deprecated_target_density_dpi = support_quirks;
  web_prefs->viewport_meta_merge_content_quirk = support_quirks;
  web_prefs->viewport_meta_non_user_scalable_quirk = support_quirks;
  web_prefs->viewport_meta_zero_values_quirk = support_quirks;
  web_prefs->clobber_user_agent_initial_scale_quirk = support_quirks;
  web_prefs->ignore_main_frame_overflow_hidden_quirk = support_quirks;
  web_prefs->report_screen_size_in_physical_pixels_quirk = support_quirks;

  web_prefs->reuse_global_for_unowned_main_frame =
      Java_AwSettings_getAllowEmptyDocumentPersistenceLocked(env, obj);

  web_prefs->password_echo_enabled =
      Java_AwSettings_getPasswordEchoEnabledLocked(env, obj);
  web_prefs->spatial_navigation_enabled =
      Java_AwSettings_getSpatialNavigationLocked(env, obj);

  bool enable_supported_hardware_accelerated_features =
      Java_AwSettings_getEnableSupportedHardwareAcceleratedFeaturesLocked(env,
                                                                          obj);
  web_prefs->accelerated_2d_canvas_enabled =
      web_prefs->accelerated_2d_canvas_enabled &&
      enable_supported_hardware_accelerated_features;
  // Always allow webgl. Webview always requires access to the GPU even if
  // it only does software draws. WebGL will not show up in software draw so
  // there is no more brokenness for user. This makes it easier for apps that
  // want to start running webgl content before webview is first attached.

  // If strict mixed content checking is enabled then running should not be
  // allowed.
  DCHECK(!Java_AwSettings_getUseStricMixedContentCheckingLocked(env, obj) ||
         !Java_AwSettings_getAllowRunningInsecureContentLocked(env, obj));
  web_prefs->allow_running_insecure_content =
      Java_AwSettings_getAllowRunningInsecureContentLocked(env, obj);
  web_prefs->strict_mixed_content_checking =
      Java_AwSettings_getUseStricMixedContentCheckingLocked(env, obj);

  web_prefs->fullscreen_supported =
      Java_AwSettings_getFullscreenSupportedLocked(env, obj);
  web_prefs->record_whole_document =
      Java_AwSettings_getRecordFullDocument(env, obj);

  // TODO(jww): This should be removed once sufficient warning has been given of
  // possible API breakage because of disabling insecure use of geolocation.
  web_prefs->allow_geolocation_on_insecure_origins =
      Java_AwSettings_getAllowGeolocationOnInsecureOrigins(env, obj);

  web_prefs->do_not_update_selection_on_mutating_selection_range =
      Java_AwSettings_getDoNotUpdateSelectionOnMutatingSelectionRange(env, obj);

  web_prefs->css_hex_alpha_color_enabled =
      Java_AwSettings_getCSSHexAlphaColorEnabledLocked(env, obj);

  // Keep spellcheck disabled on html elements unless the spellcheck="true"
  // attribute is explicitly specified. This "opt-in" behavior is for backward
  // consistency in apps that use WebView (see crbug.com/652314).
  web_prefs->spellcheck_enabled_by_default = false;

  web_prefs->scroll_top_left_interop_enabled =
      Java_AwSettings_getScrollTopLeftInteropEnabledLocked(env, obj);

  web_prefs->allow_mixed_content_upgrades =
      Java_AwSettings_getAllowMixedContentAutoupgradesLocked(env, obj);

  if (AwDarkMode* aw_dark_mode = AwDarkMode::FromWebContents(web_contents())) {
    aw_dark_mode->PopulateWebPreferences(
        web_prefs, Java_AwSettings_getForceDarkModeLocked(env, obj),
        Java_AwSettings_getForceDarkBehaviorLocked(env, obj),
        Java_AwSettings_isAlgorithmicDarkeningAllowedLocked(env, obj));
  }

  web_prefs->disable_webauthn = true;
  if (Java_AwSettings_getWebauthnSupportLocked(env, obj) != 0) {
    web_prefs->disable_webauthn = false;
  }
}

bool AwSettings::IsForceDarkApplied(JNIEnv* env,
                                    const JavaParamRef<jobject>& obj) {
  if (AwDarkMode* aw_dark_mode = AwDarkMode::FromWebContents(web_contents())) {
    return aw_dark_mode->is_force_dark_applied();
  }
  return false;
}

bool AwSettings::PrefersDarkFromTheme(JNIEnv* env,
                                      const JavaParamRef<jobject>& obj) {
  if (AwDarkMode* aw_dark_mode = AwDarkMode::FromWebContents(web_contents())) {
    return aw_dark_mode->prefers_dark_from_theme();
  }
  return false;
}

base::android::ScopedJavaLocalRef<jobject> AwSettings::GetJavaObject() {
  JNIEnv* env = base::android::AttachCurrentThread();
  return aw_settings_.get(env);
}

void AwSettings::SetEnterpriseAuthenticationAppLinkPolicyEnabled(
    JNIEnv* env,
    const JavaParamRef<jobject>& obj,
    jboolean enabled) {
  enterprise_authentication_app_link_policy_enabled_ = enabled;
}

bool AwSettings::GetEnterpriseAuthenticationAppLinkPolicyEnabled(
    JNIEnv* env,
    const JavaParamRef<jobject>& obj) {
  return enterprise_authentication_app_link_policy_enabled();
}

bool AwSettings::GetAllowFileAccess() {
  return allow_file_access_;
}

bool AwSettings::GetAllowFileAccessFromFileURLs() {
  return allow_file_access_from_file_urls_;
}

base::android::ScopedJavaLocalRef<jobjectArray>
AwSettings::UpdateXRequestedWithAllowListOriginMatcher(
    JNIEnv* env,
    const base::android::JavaParamRef<jobjectArray>& jrules) {
  std::vector<std::string> rules;
  base::android::AppendJavaStringArrayToStringVector(env, jrules, &rules);
  std::vector<std::string> bad_rules =
      xrw_allowlist_matcher_->UpdateRuleList(rules);
  return base::android::ToJavaArrayOfStrings(env, bad_rules);
}

scoped_refptr<AwContentsOriginMatcher> AwSettings::xrw_allowlist_matcher() {
  return xrw_allowlist_matcher_;
}

static jlong JNI_AwSettings_Init(JNIEnv* env,
                                 const JavaParamRef<jobject>& obj,
                                 const JavaParamRef<jobject>& web_contents) {
  content::WebContents* contents =
      content::WebContents::FromJavaWebContents(web_contents);
  AwSettings* settings = new AwSettings(env, obj, contents);
  return reinterpret_cast<intptr_t>(settings);
}

static ScopedJavaLocalRef<jobject> JNI_AwSettings_FromWebContents(
    JNIEnv* env,
    const base::android::JavaParamRef<jobject>& jweb_contents) {
  base::android::ScopedJavaLocalRef<jobject> jaw_settings;

  content::WebContents* web_contents =
      content::WebContents::FromJavaWebContents(jweb_contents);
  AwSettings* aw_settings =
      web_contents ? AwSettings::FromWebContents(web_contents) : nullptr;
  if (aw_settings) {
    jaw_settings = aw_settings->GetJavaObject();
  }
  return jaw_settings;
}

static ScopedJavaLocalRef<jstring> JNI_AwSettings_GetDefaultUserAgent(
    JNIEnv* env) {
  return base::android::ConvertUTF8ToJavaString(env, GetUserAgent());
}

static ScopedJavaLocalRef<jobject> JNI_AwSettings_GetDefaultUserAgentMetadata(
    JNIEnv* env) {
  return ToJavaAwUserAgentMetadata(
      env,
      AwClientHintsControllerDelegate::GetUserAgentMetadataOverrideBrand());
}

}  // namespace android_webview