#ifndef CONTENT_BROWSER_INDEXED_DB_INSTANCE_BUCKET_CONTEXT_H_
#define CONTENT_BROWSER_INDEXED_DB_INSTANCE_BUCKET_CONTEXT_H_
#include <stdint.h>
#include <memory>
#include <optional>
#include <queue>
#include <string>
#include "base/auto_reset.h"
#include "base/containers/flat_map.h"
#include "base/files/file_path.h"
#include "base/functional/callback.h"
#include "base/functional/callback_forward.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/sequence_checker.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "base/trace_event/memory_dump_provider.h"
#include "components/services/storage/indexed_db/locks/partitioned_lock_manager.h"
#include "components/services/storage/privileged/cpp/bucket_client_info.h"
#include "components/services/storage/privileged/mojom/indexed_db_client_state_checker.mojom.h"
#include "components/services/storage/privileged/mojom/indexed_db_control_test.mojom.h"
#include "components/services/storage/privileged/mojom/indexed_db_internals_types.mojom.h"
#include "components/services/storage/public/cpp/buckets/bucket_info.h"
#include "components/services/storage/public/cpp/quota_error_or.h"
#include "components/services/storage/public/mojom/blob_storage_context.mojom.h"
#include "components/services/storage/public/mojom/file_system_access_context.mojom.h"
#include "content/browser/indexed_db/blob_reader.h"
#include "content/browser/indexed_db/indexed_db_data_loss_info.h"
#include "content/browser/indexed_db/indexed_db_database_error.h"
#include "content/browser/indexed_db/indexed_db_external_object.h"
#include "content/browser/indexed_db/status.h"
#include "content/common/content_export.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
namespace storage {
class QuotaManagerProxy;
}
namespace content::indexed_db {
class BackingStore;
class BucketContextHandle;
class Database;
class CONTENT_EXPORT BucketContext
: public blink::mojom::IDBFactory,
public base::trace_event::MemoryDumpProvider {
public:
using DBMap = base::flat_map<std::u16string, std::unique_ptr<Database>>;
static constexpr const base::TimeDelta kBucketSpaceCacheTimeLimit =
base::Seconds(30);
enum class ClosingState {
kNotClosing,
kPreCloseGracePeriod,
kRunningPreCloseTasks,
kClosed,
};
struct CONTENT_EXPORT Delegate {
Delegate();
Delegate(Delegate&&);
~Delegate();
Delegate(const Delegate&) = delete;
Delegate& operator=(const Delegate&) = delete;
base::OnceCallback<void()> on_ready_for_destruction;
base::RepeatingCallback<void(
const storage::BucketClientInfo& ,
mojo::PendingRemote<storage::mojom::IndexedDBClientStateChecker>
,
mojo::PendingReceiver<blink::mojom::IDBFactory> )>
on_receiver_bounced;
base::RepeatingCallback<void(const std::u16string& ,
const std::u16string& )>
on_content_changed;
base::RepeatingCallback<void(bool )> on_files_written;
};
BucketContext(storage::BucketInfo bucket_info,
const base::FilePath& data_path,
Delegate&& delegate,
scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
mojo::PendingRemote<storage::mojom::BlobStorageContext>
blob_storage_context,
mojo::PendingRemote<storage::mojom::FileSystemAccessContext>
file_system_access_context);
BucketContext(const BucketContext&) = delete;
BucketContext& operator=(const BucketContext&) = delete;
~BucketContext() override;
static base::AutoReset<std::optional<bool>> OverrideShouldUseSqliteForTesting(
bool use_sqlite);
bool ShouldUseSqlite() const { return should_use_sqlite_; }
void QueueRunTasks();
bool task_run_queued() const { return task_run_queued_; }
void ForceClose(bool doom, const std::string& message);
void StartMetadataRecording();
std::vector<storage::mojom::IdbBucketMetadataPtr> StopMetadataRecording();
int64_t GetInMemorySize();
bool IsClosing() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return closing_stage_ != ClosingState::kNotClosing;
}
ClosingState closing_stage() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return closing_stage_;
}
void ReportOutstandingBlobs(bool blobs_outstanding);
void CheckCanUseDiskSpace(
int64_t space_requested,
base::OnceCallback<void(bool)> disk_space_check_callback);
void CreateAllExternalObjects(
const std::vector<IndexedDBExternalObject>& objects,
std::vector<blink::mojom::IDBExternalObjectPtr>* mojo_objects);
const storage::BucketInfo& bucket_info() { return bucket_info_; }
storage::BucketLocator bucket_locator() {
return bucket_info_.ToBucketLocator();
}
BackingStore* backing_store() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return backing_store_.get();
}
const DBMap& GetDatabasesForTesting() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return databases_;
}
PartitionedLockManager& lock_manager() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return *lock_manager_;
}
const PartitionedLockManager& lock_manager() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return *lock_manager_;
}
Delegate& delegate() { return delegate_; }
base::OneShotTimer* close_timer() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return &close_timer_;
}
base::WeakPtr<BucketContext> AsWeakPtr() {
return weak_factory_.GetWeakPtr();
}
storage::QuotaManagerProxy* quota_manager() {
return quota_manager_proxy_.get();
}
storage::mojom::BlobStorageContext* blob_storage_context() {
return blob_storage_context_.get();
}
storage::mojom::FileSystemAccessContext* file_system_access_context() {
return file_system_access_context_.get();
}
void AddReceiver(
const storage::BucketClientInfo& client_info,
mojo::PendingRemote<storage::mojom::IndexedDBClientStateChecker>
client_state_checker_remote,
mojo::PendingReceiver<blink::mojom::IDBFactory> pending_receiver);
void GetDatabaseInfo(GetDatabaseInfoCallback callback) override;
void Open(mojo::PendingAssociatedRemote<blink::mojom::IDBFactoryClient>
factory_client,
mojo::PendingAssociatedRemote<blink::mojom::IDBDatabaseCallbacks>
database_callbacks_remote,
const std::u16string& name,
int64_t version,
mojo::PendingAssociatedReceiver<blink::mojom::IDBTransaction>
transaction_receiver,
int64_t transaction_id,
int scheduling_priority) override;
void DeleteDatabase(mojo::PendingAssociatedRemote<
blink::mojom::IDBFactoryClient> factory_client,
const std::u16string& name,
bool force_close) override;
storage::mojom::IdbBucketMetadataPtr FillInMetadata(
storage::mojom::IdbBucketMetadataPtr info);
void NotifyOfIdbInternalsRelevantChange();
BucketContext* GetReferenceForTesting();
void FlushBackingStoreForTesting();
void BindMockFailureSingletonForTesting(
mojo::PendingReceiver<storage::mojom::MockFailureInjector> receiver);
void OnDatabaseError(Database* database,
Status status,
const std::string& message);
void HandleBackingStoreCorruption(const std::string& error_message);
bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
base::trace_event::ProcessMemoryDump* pmd) override;
bool in_memory() const { return data_path_.empty(); }
private:
friend BucketContextHandle;
friend class BackingStoreTestBase;
friend class DatabaseTest;
friend class IndexedDBTest;
friend class TransactionTestBase;
FRIEND_TEST_ALL_PREFIXES(IndexedDBTest, CompactionKillSwitchWorks);
FRIEND_TEST_ALL_PREFIXES(IndexedDBTest, TooLongOrigin);
FRIEND_TEST_ALL_PREFIXES(IndexedDBTest, BasicFactoryCreationAndTearDown);
FRIEND_TEST_ALL_PREFIXES(BucketContextTest, BucketSpaceDecay);
FRIEND_TEST_ALL_PREFIXES(BucketContextTest, MetadataRecordingStateHistory);
struct ReceiverContext {
ReceiverContext(
const storage::BucketClientInfo& client_info,
mojo::PendingRemote<storage::mojom::IndexedDBClientStateChecker>
client_state_checker_remote);
~ReceiverContext();
ReceiverContext(const ReceiverContext&) = delete;
ReceiverContext(ReceiverContext&&) noexcept;
ReceiverContext& operator=(const ReceiverContext&) = delete;
ReceiverContext& operator=(ReceiverContext&&) = delete;
const storage::BucketClientInfo client_info;
mojo::Remote<storage::mojom::IndexedDBClientStateChecker>
client_state_checker_remote;
};
Database* CreateAndAddDatabase(const std::u16string& name);
void OnHandleCreated();
void OnHandleDestruction();
bool CanClose();
void MaybeStartClosing();
void StartClosing();
void CloseNow();
void StartPreCloseTasks();
void RunTasks();
void OnGotBucketSpaceRemaining(storage::QuotaErrorOr<int64_t> space_left);
int64_t GetBucketSpaceToAllot();
void BindBlobReader(const IndexedDBExternalObject& blob_info,
mojo::PendingReceiver<blink::mojom::Blob> receiver);
void RemoveBoundReaders(const base::FilePath& path);
std::tuple<Status, DatabaseError, IndexedDBDataLossInfo> InitBackingStore(
bool create_if_missing);
void ResetBackingStore();
void OnReceiverDisconnected();
void RecordInternalsSnapshot();
std::string SanitizeErrorMessage(const std::string& message);
SEQUENCE_CHECKER(sequence_checker_);
const storage::BucketInfo bucket_info_;
const base::FilePath data_path_;
bool should_use_sqlite_ = false;
bool has_blobs_outstanding_ = false;
bool skip_closing_sequence_ = false;
bool running_tasks_ = false;
ClosingState closing_stage_ = ClosingState::kNotClosing;
base::OneShotTimer close_timer_;
std::unique_ptr<PartitionedLockManager> lock_manager_;
std::unique_ptr<BackingStore> backing_store_;
scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy_;
uint32_t next_database_id_for_locks_ = 0;
DBMap databases_;
int64_t open_handles_ = 0;
std::queue<std::tuple<int64_t ,
base::OnceCallback<void(bool )>>>
bucket_space_check_callbacks_;
int64_t bucket_space_remaining_ = 0;
base::TimeTicks bucket_space_remaining_timestamp_;
mojo::Remote<storage::mojom::BlobStorageContext> blob_storage_context_;
mojo::Remote<storage::mojom::FileSystemAccessContext>
file_system_access_context_;
std::map<base::FilePath,
std::tuple<std::unique_ptr<BlobReader>,
base::ScopedClosureRunner >>
file_reader_map_;
Delegate delegate_;
bool is_doomed_ = false;
bool task_run_queued_ = false;
std::vector<storage::mojom::IdbBucketMetadataPtr> metadata_recording_buffer_;
bool metadata_recording_enabled_ = false;
base::Time metadata_recording_start_time_;
mojo::ReceiverSet<blink::mojom::IDBFactory, ReceiverContext> receivers_;
base::WeakPtrFactory<BucketContext> weak_factory_{this};
};
}
#endif