chromium/chrome/browser/search_resumption/java/src/org/chromium/chrome/browser/search_resumption/SearchResumptionUserData.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.search_resumption;

import org.chromium.base.ResettersForTesting;
import org.chromium.base.UserData;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.components.embedder_support.util.UrlUtilities;
import org.chromium.components.omnibox.AutocompleteMatch;
import org.chromium.url.GURL;

import java.util.List;
import java.util.concurrent.TimeUnit;

/** Helper class for the search suggestion module which shows search suggestions on NTP. */
public class SearchResumptionUserData implements UserData {
    // The cached search suggestion results.
    static class SuggestionResult {
        private GURL mLastUrlToTrack;
        private String[] mSuggestionTexts;
        private GURL[] mSuggestionUrls;
        private List<AutocompleteMatch> mSuggestions;

        SuggestionResult(GURL gurl, String[] suggestionTexts, GURL[] suggestionUrls) {
            mLastUrlToTrack = gurl;
            mSuggestionTexts = suggestionTexts;
            mSuggestionUrls = suggestionUrls;
            mSuggestions = null;
        }

        public SuggestionResult(GURL urlToTrack, List<AutocompleteMatch> suggestions) {
            mLastUrlToTrack = urlToTrack;
            mSuggestions = suggestions;
            mSuggestionTexts = null;
            mSuggestionUrls = null;
        }

        GURL getLastUrlToTrack() {
            return mLastUrlToTrack;
        }

        String[] getSuggestionTexts() {
            return mSuggestionTexts;
        }

        GURL[] getSuggestionUrls() {
            return mSuggestionUrls;
        }

        List<AutocompleteMatch> getSuggestions() {
            return mSuggestions;
        }
    }

    private static final Class<SearchResumptionUserData> USER_DATA_KEY =
            SearchResumptionUserData.class;
    private long mTimeStampMs = -1;
    private SuggestionResult mCachedSuggestions;

    private static SearchResumptionUserData sInstance = new SearchResumptionUserData();

    /** Gets the singleton instance for the SearchResumptionUserData. */
    public static SearchResumptionUserData getInstance() {
        return sInstance;
    }

    public static void setInstanceForTesting(SearchResumptionUserData value) {
        var prevValue = sInstance;
        sInstance = value;
        ResettersForTesting.register(() -> sInstance = prevValue);
    }

    /**
     * Caches the fetched search suggestions.
     * @param tab: The tab which the suggestions live.
     * @param urlToTrack: The URL which the search suggestions are coming from.
     * @param suggestionTexts: The content texts of suggestions.
     * @param suggestionUrls: The URLs of the suggestions.
     */
    public void cacheSuggestions(
            Tab tab, GURL urlToTrack, String[] suggestionTexts, GURL[] suggestionUrls) {
        if (tab == null || !UrlUtilities.isNtpUrl(tab.getUrl())) return;

        SearchResumptionUserData searchResumptionUserData = get(tab);
        if (searchResumptionUserData == null) {
            searchResumptionUserData = new SearchResumptionUserData();
        }
        searchResumptionUserData.mTimeStampMs = System.currentTimeMillis();
        searchResumptionUserData.mCachedSuggestions =
                new SuggestionResult(urlToTrack, suggestionTexts, suggestionUrls);
        tab.getUserDataHost().setUserData(USER_DATA_KEY, searchResumptionUserData);
    }

    /**
     * Caches the fetched search suggestions.
     * @param tab The tab which the suggestions live.
     * @param urlToTrack: The URL which the search suggestions are coming from.
     * @param suggestions: The suggestions fetched using Autocompelete API.
     */
    public void cacheSuggestions(Tab tab, GURL urlToTrack, List<AutocompleteMatch> suggestions) {
        if (tab == null || !UrlUtilities.isNtpUrl(tab.getUrl())) return;

        SearchResumptionUserData searchResumptionUserData = get(tab);
        if (searchResumptionUserData == null) {
            searchResumptionUserData = new SearchResumptionUserData();
        }
        searchResumptionUserData.mTimeStampMs = System.currentTimeMillis();
        searchResumptionUserData.mCachedSuggestions = new SuggestionResult(urlToTrack, suggestions);
        tab.getUserDataHost().setUserData(USER_DATA_KEY, searchResumptionUserData);
    }

    /** Returns the last cached search suggestions, null if the UserData isn't set or expired. */
    public SuggestionResult getCachedSuggestions(Tab tab) {
        SearchResumptionUserData searchResumptionUserData = get(tab);
        return searchResumptionUserData == null
                ? null
                : searchResumptionUserData.mCachedSuggestions;
    }

    /**
     * Returns an instance of SearchResumptionUserData if exists and isn't expired, null otherwise.
     * If the data is expired, remove it.
     */
    private static SearchResumptionUserData get(Tab tab) {
        SearchResumptionUserData searchResumptionUserData =
                tab.getUserDataHost().getUserData(USER_DATA_KEY);
        if (searchResumptionUserData == null) return null;

        assert searchResumptionUserData.mTimeStampMs != -1;
        if (searchResumptionUserData.isCachedResultExpired()) {
            tab.getUserDataHost().removeUserData(USER_DATA_KEY);
            return null;
        }
        return searchResumptionUserData;
    }

    /**
     * @return whether the cached results has expired.
     */
    private boolean isCachedResultExpired() {
        return (TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis() - mTimeStampMs))
                >= ChromeFeatureList.getFieldTrialParamByFeatureAsInt(
                        ChromeFeatureList.SEARCH_RESUMPTION_MODULE_ANDROID,
                        SearchResumptionModuleUtils.TAB_EXPIRATION_TIME_PARAM,
                        SearchResumptionModuleUtils.LAST_TAB_EXPIRATION_TIME_SECONDS);
    }
}