chromium/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebSettingsAdapter.java

// Copyright 2018 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.support_lib_glue;

import static org.chromium.support_lib_glue.SupportLibWebViewChromiumFactory.recordApiCall;

import android.webkit.WebSettings;

import org.chromium.android_webview.AwDarkMode;
import org.chromium.android_webview.AwSettings;
import org.chromium.android_webview.common.MediaIntegrityApiStatus;
import org.chromium.android_webview.settings.SpeculativeLoadingAllowedFlags;
import org.chromium.base.Log;
import org.chromium.base.TraceEvent;
import org.chromium.components.webauthn.WebauthnMode;
import org.chromium.support_lib_boundary.WebSettingsBoundaryInterface;
import org.chromium.support_lib_glue.SupportLibWebViewChromiumFactory.ApiCall;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/** Adapter between WebSettingsBoundaryInterface and AwSettings. */
class SupportLibWebSettingsAdapter implements WebSettingsBoundaryInterface {
    private static final String TAG = "SupportWebSettings";
    private final AwSettings mAwSettings;

    public SupportLibWebSettingsAdapter(AwSettings awSettings) {
        mAwSettings = awSettings;
    }

    @Override
    public void setOffscreenPreRaster(boolean enabled) {
        try (TraceEvent event =
                TraceEvent.scoped(
                        "WebView.APICall.AndroidX.WEB_SETTINGS_SET_OFFSCREEN_PRE_RASTER")) {
            recordApiCall(ApiCall.WEB_SETTINGS_SET_OFFSCREEN_PRE_RASTER);
            mAwSettings.setOffscreenPreRaster(enabled);
        }
    }

    @Override
    public boolean getOffscreenPreRaster() {
        try (TraceEvent event =
                TraceEvent.scoped(
                        "WebView.APICall.AndroidX.WEB_SETTINGS_GET_OFFSCREEN_PRE_RASTER")) {
            recordApiCall(ApiCall.WEB_SETTINGS_GET_OFFSCREEN_PRE_RASTER);
            return mAwSettings.getOffscreenPreRaster();
        }
    }

    @Override
    public void setSafeBrowsingEnabled(boolean enabled) {
        try (TraceEvent event =
                TraceEvent.scoped(
                        "WebView.APICall.AndroidX.WEB_SETTINGS_SET_SAFE_BROWSING_ENABLED")) {
            recordApiCall(ApiCall.WEB_SETTINGS_SET_SAFE_BROWSING_ENABLED);
            mAwSettings.setSafeBrowsingEnabled(enabled);
        }
    }

    @Override
    public boolean getSafeBrowsingEnabled() {
        try (TraceEvent event =
                TraceEvent.scoped(
                        "WebView.APICall.AndroidX.WEB_SETTINGS_GET_SAFE_BROWSING_ENABLED")) {
            recordApiCall(ApiCall.WEB_SETTINGS_GET_SAFE_BROWSING_ENABLED);
            return mAwSettings.getSafeBrowsingEnabled();
        }
    }

    @Override
    public void setDisabledActionModeMenuItems(int menuItems) {
        try (TraceEvent event =
                TraceEvent.scoped(
                        "WebView.APICall.AndroidX.WEB_SETTINGS_SET_DISABLED_ACTION_MODE_MENU_ITEMS")) {
            recordApiCall(ApiCall.WEB_SETTINGS_SET_DISABLED_ACTION_MODE_MENU_ITEMS);
            mAwSettings.setDisabledActionModeMenuItems(menuItems);
        }
    }

    @Override
    public int getDisabledActionModeMenuItems() {
        try (TraceEvent event =
                TraceEvent.scoped(
                        "WebView.APICall.AndroidX.WEB_SETTINGS_GET_DISABLED_ACTION_MODE_MENU_ITEMS")) {
            recordApiCall(ApiCall.WEB_SETTINGS_GET_DISABLED_ACTION_MODE_MENU_ITEMS);
            return mAwSettings.getDisabledActionModeMenuItems();
        }
    }

    @Override
    public boolean getWillSuppressErrorPage() {
        try (TraceEvent event =
                TraceEvent.scoped(
                        "WebView.APICall.AndroidX.WEB_SETTINGS_GET_WILL_SUPPRESS_ERROR_PAGE")) {
            recordApiCall(ApiCall.WEB_SETTINGS_GET_WILL_SUPPRESS_ERROR_PAGE);
            return mAwSettings.getWillSuppressErrorPage();
        }
    }

    @Override
    public void setWillSuppressErrorPage(boolean suppressed) {
        try (TraceEvent event =
                TraceEvent.scoped(
                        "WebView.APICall.AndroidX.WEB_SETTINGS_SET_WILL_SUPPRESS_ERROR_PAGE")) {
            recordApiCall(ApiCall.WEB_SETTINGS_SET_WILL_SUPPRESS_ERROR_PAGE);
            mAwSettings.setWillSuppressErrorPage(suppressed);
        }
    }

    @Override
    public void setForceDark(int forceDarkMode) {
        if (AwDarkMode.isSimplifiedDarkModeEnabled()) {
            Log.w(TAG, "setForceDark() is a no-op in an app with targetSdkVersion>=T");
            return;
        }
        try (TraceEvent event =
                TraceEvent.scoped("WebView.APICall.AndroidX.WEB_SETTINGS_SET_FORCE_DARK")) {
            recordApiCall(ApiCall.WEB_SETTINGS_SET_FORCE_DARK);
            mAwSettings.setForceDarkMode(forceDarkMode);
        }
    }

    @Override
    public int getForceDark() {
        try (TraceEvent event =
                TraceEvent.scoped("WebView.APICall.AndroidX.WEB_SETTINGS_GET_FORCE_DARK")) {
            recordApiCall(ApiCall.WEB_SETTINGS_GET_FORCE_DARK);
            if (AwDarkMode.isSimplifiedDarkModeEnabled()) {
                Log.w(TAG, "getForceDark() is a no-op in an app with targetSdkVersion>=T");
                return WebSettings.FORCE_DARK_AUTO;
            }
            return mAwSettings.getForceDarkMode();
        }
    }

    @Override
    public void setForceDarkBehavior(int forceDarkBehavior) {
        try (TraceEvent event =
                TraceEvent.scoped(
                        "WebView.APICall.AndroidX.WEB_SETTINGS_SET_FORCE_DARK_BEHAVIOR")) {
            recordApiCall(ApiCall.WEB_SETTINGS_SET_FORCE_DARK_BEHAVIOR);
            if (AwDarkMode.isSimplifiedDarkModeEnabled()) {
                Log.w(TAG, "setForceDarkBehavior() is a no-op in an app with targetSdkVersion>=T");
                return;
            }
            switch (forceDarkBehavior) {
                case ForceDarkBehavior.FORCE_DARK_ONLY:
                    mAwSettings.setForceDarkBehavior(AwSettings.FORCE_DARK_ONLY);
                    break;
                case ForceDarkBehavior.MEDIA_QUERY_ONLY:
                    mAwSettings.setForceDarkBehavior(AwSettings.MEDIA_QUERY_ONLY);
                    break;
                case ForceDarkBehavior.PREFER_MEDIA_QUERY_OVER_FORCE_DARK:
                    mAwSettings.setForceDarkBehavior(AwSettings.PREFER_MEDIA_QUERY_OVER_FORCE_DARK);
                    break;
            }
        }
    }

    @Override
    public int getForceDarkBehavior() {
        try (TraceEvent event =
                TraceEvent.scoped(
                        "WebView.APICall.AndroidX.WEB_SETTINGS_GET_FORCE_DARK_BEHAVIOR")) {
            recordApiCall(ApiCall.WEB_SETTINGS_GET_FORCE_DARK_BEHAVIOR);
            if (AwDarkMode.isSimplifiedDarkModeEnabled()) {
                Log.w(TAG, "getForceDarkBehavior() is a no-op in an app with targetSdkVersion>=T");
                return ForceDarkBehavior.PREFER_MEDIA_QUERY_OVER_FORCE_DARK;
            }
            switch (mAwSettings.getForceDarkBehavior()) {
                case AwSettings.FORCE_DARK_ONLY:
                    return ForceDarkBehavior.FORCE_DARK_ONLY;
                case AwSettings.MEDIA_QUERY_ONLY:
                    return ForceDarkBehavior.MEDIA_QUERY_ONLY;
                case AwSettings.PREFER_MEDIA_QUERY_OVER_FORCE_DARK:
                    return ForceDarkBehavior.PREFER_MEDIA_QUERY_OVER_FORCE_DARK;
            }
            return ForceDarkBehavior.PREFER_MEDIA_QUERY_OVER_FORCE_DARK;
        }
    }

    @Override
    public void setAlgorithmicDarkeningAllowed(boolean allow) {
        try (TraceEvent event =
                TraceEvent.scoped(
                        "WebView.APICall.AndroidX.WEB_SETTINGS_SET_ALGORITHMIC_DARKENING_ALLOWED")) {
            recordApiCall(ApiCall.WEB_SETTINGS_SET_ALGORITHMIC_DARKENING_ALLOWED);
            if (!AwDarkMode.isSimplifiedDarkModeEnabled()) {
                Log.w(
                        TAG,
                        "setAlgorithmicDarkeningAllowed() is a no-op in an app with"
                                + "targetSdkVersion<T");
                return;
            }
            mAwSettings.setAlgorithmicDarkeningAllowed(allow);
        }
    }

    @Override
    public boolean isAlgorithmicDarkeningAllowed() {
        try (TraceEvent event =
                TraceEvent.scoped(
                        "WebView.APICall.AndroidX.WEB_SETTINGS_IS_ALGORITHMIC_DARKENING_ALLOWED")) {
            recordApiCall(ApiCall.WEB_SETTINGS_IS_ALGORITHMIC_DARKENING_ALLOWED);
            if (!AwDarkMode.isSimplifiedDarkModeEnabled()) {
                Log.w(
                        TAG,
                        "isAlgorithmicDarkeningAllowed() is a no-op in an app with "
                                + "targetSdkVersion<T");
                return false;
            }
            return mAwSettings.isAlgorithmicDarkeningAllowed();
        }
    }

    @Override
    public void setWebauthnSupport(@WebauthnSupport int support) {
        try (TraceEvent event =
                TraceEvent.scoped("WebView.APICall.AndroidX.WEB_SETTINGS_SET_WEBAUTHN_SUPPORT")) {
            recordApiCall(ApiCall.WEB_SETTINGS_SET_WEBAUTHN_SUPPORT);
            switch (support) {
                case WebauthnSupport.NONE:
                    mAwSettings.setWebauthnSupport(WebauthnMode.NONE);
                    break;
                case WebauthnSupport.APP:
                    mAwSettings.setWebauthnSupport(WebauthnMode.APP);
                    break;
                case WebauthnSupport.BROWSER:
                    mAwSettings.setWebauthnSupport(WebauthnMode.BROWSER);
                    break;
                default:
                    throw new IllegalArgumentException(
                            "Invalid WebauthnSupport specified" + support);
            }
        }
    }

    @Override
    public @WebauthnSupport int getWebauthnSupport() {
        try (TraceEvent event =
                TraceEvent.scoped("WebView.APICall.AndroidX.WEB_SETTINGS_GET_WEBAUTHN_SUPPORT")) {
            recordApiCall(ApiCall.WEB_SETTINGS_GET_WEBAUTHN_SUPPORT);
            switch (mAwSettings.getWebauthnSupport()) {
                case WebauthnMode.NONE:
                    return WebauthnSupport.NONE;
                case WebauthnMode.APP:
                    return WebauthnSupport.APP;
                case WebauthnMode.BROWSER:
                    return WebauthnSupport.BROWSER;
                default:
                    return WebauthnSupport.NONE;
            }
        }
    }

    @Override
    public void setRequestedWithHeaderOriginAllowList(Set<String> allowedOriginRules) {
        try (TraceEvent event =
                TraceEvent.scoped(
                        "WebView.APICall.AndroidX.WEB_SETTINGS_SET_REQUESTED_WITH_HEADER_ORIGIN_ALLOWLIST")) {
            recordApiCall(ApiCall.WEB_SETTINGS_SET_REQUESTED_WITH_HEADER_ORIGIN_ALLOWLIST);
            mAwSettings.setRequestedWithHeaderOriginAllowList(allowedOriginRules);
        }
    }

    @Override
    public Set<String> getRequestedWithHeaderOriginAllowList() {
        try (TraceEvent event =
                TraceEvent.scoped(
                        "WebView.APICall.AndroidX.WEB_SETTINGS_GET_REQUESTED_WITH_HEADER_ORIGIN_ALLOWLIST")) {
            recordApiCall(ApiCall.WEB_SETTINGS_GET_REQUESTED_WITH_HEADER_ORIGIN_ALLOWLIST);
            return mAwSettings.getRequestedWithHeaderOriginAllowList();
        }
    }

    @Override
    public void setEnterpriseAuthenticationAppLinkPolicyEnabled(boolean enabled) {
        try (TraceEvent event =
                TraceEvent.scoped(
                        "WebView.APICall.AndroidX.WEB_SETTINGS_SET_ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY_ENABLED")) {
            recordApiCall(
                    ApiCall.WEB_SETTINGS_SET_ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY_ENABLED);
            mAwSettings.setEnterpriseAuthenticationAppLinkPolicyEnabled(enabled);
        }
    }

    @Override
    public boolean getEnterpriseAuthenticationAppLinkPolicyEnabled() {
        try (TraceEvent event =
                TraceEvent.scoped(
                        "WebView.APICall.AndroidX.WEB_SETTINGS_GET_ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY_ENABLED")) {
            recordApiCall(
                    ApiCall.WEB_SETTINGS_GET_ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY_ENABLED);
            return mAwSettings.getEnterpriseAuthenticationAppLinkPolicyEnabled();
        }
    }

    @Override
    public void setUserAgentMetadataFromMap(Map<String, Object> uaMetadata) {
        try (TraceEvent event =
                TraceEvent.scoped(
                        "WebView.APICall.AndroidX.WEB_SETTINGS_SET_USER_AGENT_METADATA")) {
            recordApiCall(ApiCall.WEB_SETTINGS_SET_USER_AGENT_METADATA);
            mAwSettings.setUserAgentMetadataFromMap(uaMetadata);
        }
    }

    @Override
    public Map<String, Object> getUserAgentMetadataMap() {
        try (TraceEvent event =
                TraceEvent.scoped(
                        "WebView.APICall.AndroidX.WEB_SETTINGS_GET_USER_AGENT_METADATA")) {
            recordApiCall(ApiCall.WEB_SETTINGS_GET_USER_AGENT_METADATA);
            return mAwSettings.getUserAgentMetadataMap();
        }
    }

    @Override
    public void setAttributionBehavior(@AttributionBehavior int behavior) {
        try (TraceEvent event =
                TraceEvent.scoped("WebView.APICall.AndroidX.SET_ATTRIBUTION_BEHAVIOR")) {
            recordApiCall(ApiCall.SET_ATTRIBUTION_BEHAVIOR);
            switch (behavior) {
                case AttributionBehavior.DISABLED:
                    mAwSettings.setAttributionBehavior(AwSettings.ATTRIBUTION_DISABLED);
                    break;
                case AttributionBehavior.APP_SOURCE_AND_WEB_TRIGGER:
                    mAwSettings.setAttributionBehavior(
                            AwSettings.ATTRIBUTION_APP_SOURCE_AND_WEB_TRIGGER);
                    break;
                case AttributionBehavior.WEB_SOURCE_AND_WEB_TRIGGER:
                    mAwSettings.setAttributionBehavior(
                            AwSettings.ATTRIBUTION_WEB_SOURCE_AND_WEB_TRIGGER);
                    break;
                case AttributionBehavior.APP_SOURCE_AND_APP_TRIGGER:
                    mAwSettings.setAttributionBehavior(
                            AwSettings.ATTRIBUTION_APP_SOURCE_AND_APP_TRIGGER);
                    break;
            }
        }
    }

    @Override
    public int getAttributionBehavior() {
        try (TraceEvent event =
                TraceEvent.scoped("WebView.APICall.AndroidX.GET_ATTRIBUTION_BEHAVIOR")) {
            recordApiCall(ApiCall.GET_ATTRIBUTION_BEHAVIOR);
            switch (mAwSettings.getAttributionBehavior()) {
                case AwSettings.ATTRIBUTION_DISABLED:
                    return AttributionBehavior.DISABLED;
                case AwSettings.ATTRIBUTION_APP_SOURCE_AND_WEB_TRIGGER:
                    return AttributionBehavior.APP_SOURCE_AND_WEB_TRIGGER;
                case AwSettings.ATTRIBUTION_WEB_SOURCE_AND_WEB_TRIGGER:
                    return AttributionBehavior.WEB_SOURCE_AND_WEB_TRIGGER;
                case AwSettings.ATTRIBUTION_APP_SOURCE_AND_APP_TRIGGER:
                    return AttributionBehavior.APP_SOURCE_AND_APP_TRIGGER;
            }
            return AttributionBehavior.APP_SOURCE_AND_WEB_TRIGGER;
        }
    }

    @Override
    public void setWebViewMediaIntegrityApiStatus(
            @WebViewMediaIntegrityApiStatus int defaultStatus,
            Map<String, @WebViewMediaIntegrityApiStatus Integer> permissionConfig) {
        try (TraceEvent event =
                TraceEvent.scoped(
                        "WebView.APICall.AndroidX.SET_WEBVIEW_MEDIA_INTEGRITY_API_STATUS")) {
            recordApiCall(ApiCall.SET_WEBVIEW_MEDIA_INTEGRITY_API_STATUS);

            Map<String, @MediaIntegrityApiStatus Integer> permissionToApiStatus = new HashMap<>();
            for (Map.Entry<String, @WebViewMediaIntegrityApiStatus Integer> entry :
                    permissionConfig.entrySet()) {
                permissionToApiStatus.put(entry.getKey(), convertToApiStatus(entry.getValue()));
            }
            @MediaIntegrityApiStatus int defaultApiStatus = convertToApiStatus(defaultStatus);
            mAwSettings.setWebViewIntegrityApiStatus(defaultApiStatus, permissionToApiStatus);
        }
    }

    @Override
    public @WebViewMediaIntegrityApiStatus int getWebViewMediaIntegrityApiDefaultStatus() {
        try (TraceEvent event =
                TraceEvent.scoped(
                        "WebView.APICall.AndroidX.GET_WEBVIEW_MEDIA_INTEGRITY_API_DEFAULT_STATUS")) {
            recordApiCall(ApiCall.GET_WEBVIEW_MEDIA_INTEGRITY_API_DEFAULT_STATUS);
            return convertFromApiStatus(mAwSettings.getWebViewIntegrityApiDefaultStatus());
        }
    }

    @Override
    public Map<String, @WebViewMediaIntegrityApiStatus Integer>
            getWebViewMediaIntegrityApiOverrideRules() {
        try (TraceEvent event =
                TraceEvent.scoped(
                        "WebView.APICall.AndroidX.GET_WEBVIEW_MEDIA_INTEGRITY_API_OVERRIDE_RULES")) {
            recordApiCall(ApiCall.GET_WEBVIEW_MEDIA_INTEGRITY_API_OVERRIDE_RULES);
            Map<String, @MediaIntegrityApiStatus Integer> overrideRules = new HashMap<>();
            for (Map.Entry<String, @MediaIntegrityApiStatus Integer> entry :
                    mAwSettings.getWebViewIntegrityApiOverrideRules().entrySet()) {
                overrideRules.put(entry.getKey(), convertFromApiStatus(entry.getValue()));
            }
            return overrideRules;
        }
    }

    private @MediaIntegrityApiStatus Integer convertToApiStatus(
            @WebViewMediaIntegrityApiStatus int status) {
        switch (status) {
            case WebViewMediaIntegrityApiStatus.DISABLED:
                return MediaIntegrityApiStatus.DISABLED;
            case WebViewMediaIntegrityApiStatus.ENABLED_WITHOUT_APP_IDENTITY:
                return MediaIntegrityApiStatus.ENABLED_WITHOUT_APP_IDENTITY;
            case WebViewMediaIntegrityApiStatus.ENABLED:
                return MediaIntegrityApiStatus.ENABLED;
        }
        throw new IllegalArgumentException("Invalid WebView Media Integrity API status: " + status);
    }

    private @WebViewMediaIntegrityApiStatus Integer convertFromApiStatus(
            @MediaIntegrityApiStatus int status) {
        switch (status) {
            case MediaIntegrityApiStatus.DISABLED:
                return WebViewMediaIntegrityApiStatus.DISABLED;
            case MediaIntegrityApiStatus.ENABLED_WITHOUT_APP_IDENTITY:
                return WebViewMediaIntegrityApiStatus.ENABLED_WITHOUT_APP_IDENTITY;
            case MediaIntegrityApiStatus.ENABLED:
                return WebViewMediaIntegrityApiStatus.ENABLED;
        }
        // unreached
        throw new IllegalArgumentException("Invalid WebView Media Integrity API status: " + status);
    }

    @Override
    public void setSpeculativeLoadingStatus(
            @SpeculativeLoadingStatus int speculativeLoadingStatus) {
        try (TraceEvent event =
                TraceEvent.scoped("WebView.APICall.AndroidX.SET_SPECULATIVE_LOADING_ENABLED")) {
            recordApiCall(ApiCall.SET_SPECULATIVE_LOADING_STATUS);
            switch (speculativeLoadingStatus) {
                case SpeculativeLoadingStatus.DISABLED:
                    mAwSettings.setSpeculativeLoadingAllowed(
                            SpeculativeLoadingAllowedFlags.SPECULATIVE_LOADING_DISABLED);
                    break;
                case SpeculativeLoadingStatus.PRERENDER_ENABLED:
                    mAwSettings.setSpeculativeLoadingAllowed(
                            SpeculativeLoadingAllowedFlags.PRERENDER_ENABLED);
                    break;
            }
        }
    }

    @Override
    public @SpeculativeLoadingStatus int getSpeculativeLoadingStatus() {
        try (TraceEvent event =
                TraceEvent.scoped("WebView.APICall.AndroidX.IS_SPECULATIVE_LOADING_ENABLED")) {
            recordApiCall(ApiCall.GET_SPECULATIVE_LOADING_STATUS);

            switch (mAwSettings.getSpeculativeLoadingAllowed()) {
                case SpeculativeLoadingAllowedFlags.SPECULATIVE_LOADING_DISABLED:
                    return SpeculativeLoadingStatus.DISABLED;
                case SpeculativeLoadingAllowedFlags.PRERENDER_ENABLED:
                    return SpeculativeLoadingStatus.PRERENDER_ENABLED;
            }
        }
        // It has a default state so theoretically this case shouldn't happen.
        throw new IllegalArgumentException("Couldn't retrieve a valid status.");
    }

    @Override
    public void setBackForwardCacheEnabled(boolean backForwardCacheEnabled) {
        try (TraceEvent ignored =
                TraceEvent.scoped("WebView.APICall.AndroidX.SET_BACK_FORWARD_CACHE_ENABLED")) {
            recordApiCall(ApiCall.SET_BACK_FORWARD_CACHE_ENABLED);
            mAwSettings.setBackForwardCacheEnabled(backForwardCacheEnabled);
        }
    }

    @Override
    public boolean getBackForwardCacheEnabled() {
        try (TraceEvent ignored =
                TraceEvent.scoped("WebView.APICall.AndroidX.GET_BACK_FORWARD_CACHE_ENABLED")) {
            recordApiCall(ApiCall.GET_BACK_FORWARD_CACHE_ENABLED);
            return mAwSettings.getBackForwardCacheEnabled();
        }
    }
}