chromium/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/TabStateDirectory.java

// Copyright 2020 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.tabpersistence;

import android.content.Context;

import androidx.annotation.VisibleForTesting;

import org.chromium.base.ContextUtils;
import org.chromium.base.Log;
import org.chromium.base.ResettersForTesting;

import java.io.File;

/**
 * Manages the directory where tab state is saved.
 *
 * <p>TODO(crbug.com/40136597): Deduplicate code between tabbed mode and custom tabs.
 */
public class TabStateDirectory {
    private static final String TAG = "tabpersistence";

    /** The name of the base directory where the state is saved. */
    private static final String BASE_STATE_FOLDER = "tabs";

    /** The name of the directory where the state for tabbed mode is saved. */
    @VisibleForTesting public static final String TABBED_MODE_DIRECTORY = "0";

    /** The name of the directory where the state for custom tabs is saved. */
    public static final String CUSTOM_TABS_DIRECTORY = "custom_tabs";

    /** Prevents two state directories from getting created simultaneously. */
    private static final Object TABBED_MODE_DIR_CREATION_LOCK = new Object();

    /** Prevents two state directories from getting created simultaneously. */
    private static final Object CUSTOM_TABS_DIR_CREATION_LOCK = new Object();

    private static File sTabbedModeStateDirectory;
    private static File sCustomTabsStateDirectory;

    /**
     * The folder where the state should be saved to.
     * @return A file representing the directory that contains TabModelSelector states.
     */
    public static File getOrCreateTabbedModeStateDirectory() {
        synchronized (TABBED_MODE_DIR_CREATION_LOCK) {
            if (sTabbedModeStateDirectory == null) {
                sTabbedModeStateDirectory =
                        new File(getOrCreateBaseStateDirectory(), TABBED_MODE_DIRECTORY);
                if (!sTabbedModeStateDirectory.exists() && !sTabbedModeStateDirectory.mkdirs()) {
                    Log.e(TAG, "Failed to create state folder: " + sTabbedModeStateDirectory);
                }
            }
        }
        return sTabbedModeStateDirectory;
    }

    /**
     * The folder where the state should be saved to.
     * @return A file representing the directory that contains TabModelSelector states.
     */
    public static File getOrCreateCustomTabModeStateDirectory() {
        synchronized (CUSTOM_TABS_DIR_CREATION_LOCK) {
            if (sCustomTabsStateDirectory == null) {
                sCustomTabsStateDirectory =
                        new File(getOrCreateBaseStateDirectory(), CUSTOM_TABS_DIRECTORY);
                if (!sCustomTabsStateDirectory.exists() && !sCustomTabsStateDirectory.mkdirs()) {
                    Log.e(TAG, "Failed to create state folder: " + sCustomTabsStateDirectory);
                }
            }
        }
        return sCustomTabsStateDirectory;
    }

    private static class BaseStateDirectoryHolder {
        // Not final for tests.
        private static File sDirectory;

        static {
            sDirectory =
                    ContextUtils.getApplicationContext()
                            .getDir(BASE_STATE_FOLDER, Context.MODE_PRIVATE);
        }
    }

    /**
     * Directory containing all data for TabModels.  Each subdirectory stores info about different
     * TabModelSelectors, including metadata about each TabModel and TabStates for each of their
     * tabs.
     *
     * @return The parent state directory.
     */
    public static File getOrCreateBaseStateDirectory() {
        return BaseStateDirectoryHolder.sDirectory;
    }

    /** Sets where the base state directory is in tests. */
    public static void setBaseStateDirectoryForTests(File directory) {
        var oldValue = BaseStateDirectoryHolder.sDirectory;
        BaseStateDirectoryHolder.sDirectory = directory;
        ResettersForTesting.register(() -> BaseStateDirectoryHolder.sDirectory = oldValue);
    }

    public static void resetTabbedModeStateDirectoryForTesting() {
        sTabbedModeStateDirectory = null;
    }
}