chromium/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/ArchivePersistedTabData.java

// Copyright 2024 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.tab.state;

import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;

import com.google.protobuf.InvalidProtocolBufferException;

import org.chromium.base.Callback;
import org.chromium.base.Log;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.proto.ArchivePersistedTabData.ArchivePersistedTabDataProto;

import java.nio.ByteBuffer;

/** {@link PersistedTabData} for archiving/auto-deleting inactive tabs. */
public class ArchivePersistedTabData extends PersistedTabData {
    @VisibleForTesting protected static final long INVALID_TIMESTAMP = -1;
    private static final String TAG = "ArchivePTD";
    private static final Class<ArchivePersistedTabData> USER_DATA_KEY =
            ArchivePersistedTabData.class;

    private long mArchivedTimeMs = INVALID_TIMESTAMP;

    /**
     * Gets the {@link ArchivePersistedTabData} associated with the given tab or creates one if it
     * doesn't exist. If the tab data is created, then the current timestamp is used as the archive
     * time.
     *
     * @param tab The {@link Tab} to get/create the tab data for.
     * @param callback The {@link Callback} to be invoked when the {@link ArchivePersistedTabData}
     *     is ready.
     */
    public static void from(Tab tab, Callback<ArchivePersistedTabData> callback) {
        PersistedTabData.from(tab, () -> new ArchivePersistedTabData(tab), USER_DATA_KEY, callback);
    }

    @VisibleForTesting
    protected static ArchivePersistedTabData from(Tab tab) {
        if (tab.getUserDataHost().getUserData(USER_DATA_KEY) == null) {
            tab.getUserDataHost().setUserData(USER_DATA_KEY, new ArchivePersistedTabData(tab));
        }
        return tab.getUserDataHost().getUserData(USER_DATA_KEY);
    }

    private ArchivePersistedTabData(Tab tab) {
        super(
                tab,
                PersistedTabDataConfiguration.get(ArchivePersistedTabData.class, tab.isIncognito())
                        .getStorage(),
                PersistedTabDataConfiguration.get(ArchivePersistedTabData.class, tab.isIncognito())
                        .getId());
    }

    public long getArchivedTimeMs() {
        return mArchivedTimeMs;
    }

    public void setArchivedTimeMs(long archivedTimeMs) {
        mArchivedTimeMs = archivedTimeMs;
        save();
    }

    // PersistedTabData implementation.

    @Override
    Serializer<ByteBuffer> getSerializer() {
        return () ->
                ArchivePersistedTabDataProto.newBuilder()
                        .setArchivedTimeMs(mArchivedTimeMs)
                        .build()
                        .toByteString()
                        .asReadOnlyByteBuffer();
    }

    @Override
    boolean deserialize(@Nullable ByteBuffer bytes) {
        if (bytes == null || bytes.limit() == 0) return false;

        try {
            mArchivedTimeMs = ArchivePersistedTabDataProto.parseFrom(bytes).getArchivedTimeMs();
        } catch (InvalidProtocolBufferException e) {
            Log.i(TAG, "deserialize failed: \n" + e.toString());
            return false;
        }

        return true;
    }

    @Override
    public String getUmaTag() {
        return TAG;
    }
}