blob: 0f7276db6a7287da3a2c34e96d2571f9674cd60d [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/android/tab_storage_packager_android.h"
#include <jni.h>
#include <cstdint>
#include <memory>
#include <utility>
#include <vector>
#include "base/android/jni_android.h"
#include "base/android/jni_bytebuffer.h"
#include "base/android/jni_string.h"
#include "base/memory/ptr_util.h"
#include "base/token.h"
#include "chrome/browser/android/tab_android.h"
#include "chrome/browser/android/tab_android_conversions.h"
#include "chrome/browser/android/tab_group_android.h"
#include "chrome/browser/android/tab_group_features.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/tab/android_tab_package.h"
#include "chrome/browser/tab/payload.h"
#include "chrome/browser/tab/protocol/tab_group_collection_state.pb.h"
#include "chrome/browser/tab/protocol/tab_strip_collection_state.pb.h"
#include "chrome/browser/tab/storage_id_mapping.h"
#include "chrome/browser/tab/storage_package.h"
#include "chrome/browser/tab/tab_storage_package.h"
#include "chrome/browser/tab/tab_storage_packager.h"
#include "components/tabs/public/android/jni_conversion.h"
#include "components/tabs/public/tab_strip_collection.h"
// Must come after all headers that specialize FromJniType() / ToJniType().
#include "chrome/android/chrome_jni_headers/TabStoragePackager_jni.h"
namespace tabs {
// TODO(crbug.com/430996004): Reference a shared constant for the web content
// state.
static const int kTabStoragePackagerAndroidVersion = 2;
// A payload of data representing TabStripCollection.
class TabStripCollectionStorageData : public Payload {
public:
explicit TabStripCollectionStorageData(tabs_pb::TabStripCollectionState state)
: state_(std::move(state)) {}
~TabStripCollectionStorageData() override = default;
std::vector<uint8_t> SerializePayload() const override {
std::vector<uint8_t> payload_vec(state_.ByteSizeLong());
state_.SerializeToArray(payload_vec.data(), payload_vec.size());
return payload_vec;
}
private:
tabs_pb::TabStripCollectionState state_;
};
// A wrapper around TabStripCollectionState that has not had a StorageIdMapping
// applied to it so the data is still unmapped (i.e. we have references to
// objects that need to be converted to storage ids).
class UnmappedTabStripCollectionStorageData {
public:
UnmappedTabStripCollectionStorageData(TabAndroid* active_tab,
tabs_pb::TabStripCollectionState state)
: active_tab_(active_tab), state_(std::move(state)) {}
~UnmappedTabStripCollectionStorageData() = default;
TabAndroid* active_tab() const { return active_tab_.get(); }
// Moves the state out of this object. This should only be called once.
tabs_pb::TabStripCollectionState TakeState() {
CHECK(is_valid_) << "Attempting to take state multiple times.";
is_valid_ = false;
return std::move(state_);
}
private:
bool is_valid_{true};
// May be nullptr if there is no active tab in the collection (i.e. there are
// no tabs in the tab strip).
raw_ptr<TabAndroid> active_tab_;
tabs_pb::TabStripCollectionState state_;
};
// Consumes `unmapped_data` and applies the `mapping` to it. The returned
// TabStripCollectionStorageData is a valid payload that can be packaged into
// the database.
std::unique_ptr<TabStripCollectionStorageData>
MapAndConsumeUnmappedTabStripCollectionStorageData(
std::unique_ptr<UnmappedTabStripCollectionStorageData> unmapped_data,
StorageIdMapping& mapping) {
tabs_pb::TabStripCollectionState state = unmapped_data->TakeState();
TabAndroid* active_tab = unmapped_data->active_tab();
if (active_tab) {
tabs_pb::Token* active_tab_storage_id =
state.mutable_active_tab_storage_id();
StorageIdToTokenProto(mapping.GetStorageId(active_tab),
active_tab_storage_id);
}
return std::make_unique<TabStripCollectionStorageData>(std::move(state));
}
TabStoragePackagerAndroid::TabStoragePackagerAndroid(Profile* profile)
: profile_(profile) {
JNIEnv* env = base::android::AttachCurrentThread();
java_obj_.Reset(
Java_TabStoragePackager_create(env, reinterpret_cast<intptr_t>(this)));
}
bool TabStoragePackagerAndroid::IsOffTheRecord(
const TabCollection* collection) const {
const TabCollection* root_collection = GetRootCollection(collection);
JNIEnv* env = base::android::AttachCurrentThread();
return static_cast<bool>(Java_TabStoragePackager_isOffTheRecord(
env, java_obj_, profile_,
static_cast<const TabStripCollection*>(root_collection)));
}
std::string TabStoragePackagerAndroid::GetWindowTag(
const TabCollection* collection) const {
const TabCollection* root_collection = GetRootCollection(collection);
JNIEnv* env = base::android::AttachCurrentThread();
return Java_TabStoragePackager_getWindowTag(
env, java_obj_, profile_,
static_cast<const TabStripCollection*>(root_collection));
}
std::unique_ptr<StoragePackage> TabStoragePackagerAndroid::Package(
const TabInterface* tab) {
JNIEnv* env = base::android::AttachCurrentThread();
long ptr_value = Java_TabStoragePackager_packageTab(env, java_obj_,
ToTabAndroidChecked(tab));
TabStoragePackage* data = reinterpret_cast<TabStoragePackage*>(ptr_value);
return base::WrapUnique(data);
}
std::unique_ptr<Payload>
TabStoragePackagerAndroid::PackageTabStripCollectionData(
const TabStripCollection* collection,
StorageIdMapping& mapping) {
JNIEnv* env = base::android::AttachCurrentThread();
long ptr_value = Java_TabStoragePackager_packageTabStripCollection(
env, java_obj_, profile_, collection);
return MapAndConsumeUnmappedTabStripCollectionStorageData(
base::WrapUnique(
reinterpret_cast<UnmappedTabStripCollectionStorageData*>(ptr_value)),
mapping);
}
long TabStoragePackagerAndroid::ConsolidateTabData(
JNIEnv* env,
jlong timestamp_millis,
const jni_zero::JavaRef<jobject>& web_contents_state_buffer,
std::optional<std::string> opener_app_id,
jint theme_color,
jlong last_navigation_committed_timestamp_millis,
jboolean tab_has_sensitive_content,
TabAndroid* tab) {
std::optional<std::vector<uint8_t>> web_contents_state_bytes;
if (web_contents_state_buffer) {
base::span<const uint8_t> span =
base::android::JavaByteBufferToSpan(env, web_contents_state_buffer);
web_contents_state_bytes.emplace(span.begin(), span.end());
}
base::Token tab_group_id;
if (tab->GetGroup().has_value()) {
tab_group_id = tab->GetGroup()->token();
}
AndroidTabPackage android_package(
kTabStoragePackagerAndroidVersion, tab->GetAndroidId(),
tab->GetParentId(), timestamp_millis, std::move(web_contents_state_bytes),
std::move(opener_app_id), theme_color,
last_navigation_committed_timestamp_millis, tab_has_sensitive_content,
tab->GetTabLaunchTypeAtCreation());
TabStoragePackage* package_ptr =
new TabStoragePackage(tab->GetUserAgent(), std::move(tab_group_id),
tab->IsPinned(), std::move(android_package));
return reinterpret_cast<long>(package_ptr);
}
long TabStoragePackagerAndroid::ConsolidateTabStripCollectionData(
JNIEnv* env,
jint window_id,
jint j_tab_model_type,
TabAndroid* active_tab) {
tabs_pb::TabStripCollectionState state;
state.set_window_id(window_id);
state.set_tab_model_type(j_tab_model_type);
UnmappedTabStripCollectionStorageData* data =
new UnmappedTabStripCollectionStorageData(active_tab, std::move(state));
return reinterpret_cast<long>(data);
}
TabStoragePackagerAndroid::~TabStoragePackagerAndroid() = default;
} // namespace tabs
DEFINE_JNI(TabStoragePackager)