chromium/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageTabData.java

// Copyright 2022 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.chrome.browser.offlinepages;

import androidx.annotation.NonNull;

import org.chromium.base.UserData;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.chrome.browser.tab.Tab;

/**
 * UserData object that holds the OfflinePage visibility status for the associated tab. This can be
 * used to check the offline status of a tab without calling into native.
 */
public class OfflinePageTabData implements UserData {
    private static final Class<OfflinePageTabData> USER_DATA_KEY = OfflinePageTabData.class;
    private static final double SAMPLE_PROBABILITY = 0.01;

    /**
     * Returns whether the given tab is showing an offline page. Returns {@code false} for a {@code
     * null} or un-initialized tab.
     */
    public static boolean isShowingOfflinePage(Tab tab) {
        if (tab == null || !tab.isInitialized()) return false;
        OfflinePageTabData offlinePageTabData = get(tab);
        if (offlinePageTabData == null) return false;
        boolean result = offlinePageTabData.isTabShowingOfflinePage();
        // Check to see if the cached result is correct, but only once every 100 calls.
        if (Math.random() < SAMPLE_PROBABILITY) {
            boolean freshResult = OfflinePageUtils.isOfflinePage(tab);
            RecordHistogram.recordBooleanHistogram(
                    "OfflinePages.CachedOfflineStatusValid", freshResult == result);
            result = freshResult;
        }

        return result;
    }

    /**
     * Returns whether the given tab is showing a trusted offline page. Returns {@code false} for a
     * {@code null} or un-initialized tab.
     */
    public static boolean isShowingTrustedOfflinePage(Tab tab) {
        if (tab == null || !tab.isInitialized()) return false;
        OfflinePageTabData offlinePageTabData = get(tab);
        return offlinePageTabData != null && offlinePageTabData.isTabShowingTrustedOfflinePage();
    }

    /**
     * Returns the OfflinePageTabData instance for the given Tab object, creating it if does not yet
     * exist. This can never return {@code null}, but is not safe to call if the tab has been
     * destroyed.
     */
    static OfflinePageTabData from(@NonNull Tab tab) {
        assert tab.isInitialized();
        OfflinePageTabData offlinePageTabData = get(tab);
        if (offlinePageTabData == null) {
            offlinePageTabData =
                    tab.getUserDataHost().setUserData(USER_DATA_KEY, new OfflinePageTabData());
        }
        return offlinePageTabData;
    }

    private static OfflinePageTabData get(Tab tab) {
        return tab.getUserDataHost().getUserData(USER_DATA_KEY);
    }

    private boolean mIsTabShowingOfflinePage;
    private boolean mIsTabShowingTrustedOfflinePage;

    OfflinePageTabData() {}

    public boolean isTabShowingOfflinePage() {
        return mIsTabShowingOfflinePage;
    }

    public boolean isTabShowingTrustedOfflinePage() {
        return mIsTabShowingTrustedOfflinePage;
    }

    void setIsTabShowingOfflinePage(boolean isTabShowingOfflinePage) {
        mIsTabShowingOfflinePage = isTabShowingOfflinePage;
    }

    void setIsTabShowingTrustedOfflinePage(boolean isTabShowingTrustedOfflinePage) {
        mIsTabShowingTrustedOfflinePage = isTabShowingTrustedOfflinePage;
    }
}