chromium/android_webview/java/src/org/chromium/android_webview/AwWebContentsMetricsRecorder.java

// Copyright 2021 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.content.Context;

import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;

import org.chromium.android_webview.common.Lifetime;
import org.chromium.android_webview.settings.ForceDarkBehavior;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.content_public.browser.LoadCommittedDetails;
import org.chromium.content_public.browser.WebContents;
import org.chromium.content_public.browser.WebContentsObserver;

import java.lang.ref.WeakReference;
import java.util.Set;

/**
 * This class records WebView settings usage.
 *
 * It records histograms at navigationEntryCommitted to show what the settings were on navigation.
 * It also offers static helpers to record the settings as they are configured by the embedding
 * application.
 */
@Lifetime.WebView
public class AwWebContentsMetricsRecorder extends WebContentsObserver {
    private WeakReference<Context> mContext;
    private WeakReference<AwSettings> mAwSettings;

    public AwWebContentsMetricsRecorder(
            WebContents webContents, Context context, AwSettings awSettings) {
        super(webContents);
        mContext = new WeakReference<Context>(context);
        mAwSettings = new WeakReference<AwSettings>(awSettings);
    }

    @Override
    public void navigationEntryCommitted(LoadCommittedDetails details) {
        if (!details.isMainFrame()) return;
        recordDarkModeMetrics();
        recordRequestedWithHeaderMetrics();
    }

    private void recordDarkModeMetrics() {
        Context context = mContext.get();
        if (context == null) return;

        AwSettings awSettings = mAwSettings.get();
        if (awSettings == null) return;

        int nightMode = DarkModeHelper.getNightMode(context);
        int lightTheme = DarkModeHelper.getLightTheme(context);
        boolean isForceDarkApplied = awSettings.isForceDarkApplied();
        int forceDarkMode = awSettings.getForceDarkMode();
        int forceDarkBehavior = awSettings.getForceDarkBehavior();
        int textLuminance = DarkModeHelper.getPrimaryTextLuminace(context);
        recordDarkModeMetrics(
                nightMode,
                lightTheme,
                isForceDarkApplied,
                forceDarkMode,
                forceDarkBehavior,
                textLuminance);
    }

    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    public static void recordDarkModeMetrics(
            int nightMode,
            int lightTheme,
            boolean isForceDarkApplied,
            int forceDarkMode,
            int forceDarkBehavior,
            int textLuminance) {
        RecordHistogram.recordEnumeratedHistogram(
                "Android.WebView.DarkMode.ForceDarkBehavior",
                forceDarkBehavior,
                AwSettings.FORCE_DARK_STRATEGY_COUNT);
        RecordHistogram.recordEnumeratedHistogram(
                "Android.WebView.DarkMode.ForceDarkMode",
                forceDarkMode,
                AwSettings.FORCE_DARK_MODES_COUNT);
        RecordHistogram.recordBooleanHistogram(
                "Android.WebView.DarkMode.InDarkMode", isForceDarkApplied);
        // Refer to WebViewInDarkModeVsLightTheme in enums.xml.
        RecordHistogram.recordEnumeratedHistogram(
                "Android.WebView.DarkMode.InDarkModeVsLightTheme",
                (isForceDarkApplied ? 0 : 1) * DarkModeHelper.LightTheme.LIGHT_THEME_COUNT
                        + lightTheme,
                2 * DarkModeHelper.LightTheme.LIGHT_THEME_COUNT);
        // Refer to WebViewInDarkModeVsNightMode in enums.xml.
        RecordHistogram.recordEnumeratedHistogram(
                "Android.WebView.DarkMode.InDarkModeVsNightMode",
                (isForceDarkApplied ? 0 : 1) * DarkModeHelper.NightMode.NIGHT_MODE_COUNT
                        + nightMode,
                2 * DarkModeHelper.NightMode.NIGHT_MODE_COUNT);
        RecordHistogram.recordEnumeratedHistogram(
                "Android.WebView.DarkMode.LightTheme",
                lightTheme,
                DarkModeHelper.LightTheme.LIGHT_THEME_COUNT);
        RecordHistogram.recordEnumeratedHistogram(
                "Android.WebView.DarkMode.PrimaryTextLuminanceVsLightTheme",
                textLuminance * DarkModeHelper.LightTheme.LIGHT_THEME_COUNT + lightTheme,
                DarkModeHelper.TextLuminance.TEXT_LUMINACE_COUNT
                        * DarkModeHelper.LightTheme.LIGHT_THEME_COUNT);
        RecordHistogram.recordEnumeratedHistogram(
                "Android.WebView.DarkMode.PrimaryTextLuminanceVsNightMode",
                textLuminance * DarkModeHelper.NightMode.NIGHT_MODE_COUNT + nightMode,
                DarkModeHelper.TextLuminance.TEXT_LUMINACE_COUNT
                        * DarkModeHelper.NightMode.NIGHT_MODE_COUNT);
        RecordHistogram.recordEnumeratedHistogram(
                "Android.WebView.DarkMode.NightMode",
                nightMode,
                DarkModeHelper.NightMode.NIGHT_MODE_COUNT);
        RecordHistogram.recordEnumeratedHistogram(
                "Android.WebView.DarkMode.NightModeVsLightTheme",
                nightMode * DarkModeHelper.LightTheme.LIGHT_THEME_COUNT + lightTheme,
                DarkModeHelper.NightMode.NIGHT_MODE_COUNT
                        * DarkModeHelper.LightTheme.LIGHT_THEME_COUNT);
    }

    private void recordRequestedWithHeaderMetrics() {
        AwSettings awSettings = mAwSettings.get();
        if (awSettings == null) return;
        Set<String> allowList = awSettings.getRequestedWithHeaderOriginAllowList();
        RecordHistogram.recordCount1000Histogram(
                "Android.WebView.RequestedWithHeader.OnNavigationRequestedWithHeaderAllowListSize",
                allowList.size());
    }

    public static void recordForceDarkModeAPIUsage(Context context, int forceDarkMode) {
        int value =
                DarkModeHelper.getNightMode(context) * AwSettings.FORCE_DARK_MODES_COUNT
                        + forceDarkMode;
        System.out.println("recordForce value " + value);
        RecordHistogram.recordEnumeratedHistogram(
                "Android.WebView.ForceDarkMode",
                value,
                DarkModeHelper.NightMode.NIGHT_MODE_COUNT * AwSettings.FORCE_DARK_MODES_COUNT);
    }

    public static void recordForceDarkBehaviorAPIUsage(@ForceDarkBehavior int forceDarkBehavior) {
        RecordHistogram.recordEnumeratedHistogram(
                "Android.WebView.ForceDarkBehavior",
                forceDarkBehavior,
                AwSettings.FORCE_DARK_STRATEGY_COUNT);
    }

    public static void recordRequestedWithHeaderModeAPIUsage(@NonNull Set<String> originAllowList) {
        RecordHistogram.recordCount1000Histogram(
                "Android.WebView.RequestedWithHeader.SetRequestedWithHeaderModeAllowListSize",
                originAllowList.size());
    }

    public static void recordRequestedWithHeaderModeServiceWorkerAPIUsage(
            @NonNull Set<String> originAllowList) {
        RecordHistogram.recordCount1000Histogram(
                "Android.WebView.RequestedWithHeader."
                        + "SetServiceWorkerRequestedWithHeaderModeAllowListSize",
                originAllowList.size());
    }
}