Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions docs/api/session.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ of an existing `Session` object.

* `path` string
* `options` Object (optional)
* `cache` boolean - Whether to enable cache.
* `cache` boolean (optional) - Whether to enable cache.
* `quota` number (optional) - Use a quota size for a partition (in bytes).

Returns `Session` - A session instance from the absolute path as specified by the `path`
string. When there is an existing `Session` with the same absolute path, it
Expand All @@ -56,7 +57,11 @@ be thrown if an empty string is provided.

To create a `Session` with `options`, you have to ensure the `Session` with the
`path` has never been used before. There is no way to change the `options`
of an existing `Session` object.
of an existing `Session` object. The optional `quota` parameter can be used to
specify the a quota size that can be used by the session, which will be returned as the quota provided by navigator.storage.estimate(). If the `quota` is not
provided, the size of the volume on which path is located will be used. A smaller
quota size will allow the LRU algorithm to evict more data from the cache, though there
is no guarantee that the cache will not exceed the quota size.

## Properties

Expand Down
1 change: 1 addition & 0 deletions patches/chromium/.patches
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,4 @@ chore_patch_out_profile_methods_in_profile_selections_cc.patch
add_gin_converter_support_for_arraybufferview.patch
chore_defer_usb_service_getdevices_request_until_usb_service_is.patch
revert_roll_clang_rust_llvmorg-16-init-17653-g39da55e8-3.patch
feat_add_storage_quota_capability_for_partitions.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: George Kottackal <kottackal.george@gmail.com>
Date: Sun, 2 Apr 2023 23:04:40 +0100
Subject: feat: add storage quota capability for partitions

This patch adds the capability for a storage quota
to be set on partitions.This capability will be
exercised only when physical storage based
partitions are used.

diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc
index e5185febf2cc3bc28353a0ec3e2c57aa536a3303..80989ba382778819b912b5bb9904a583c958215d 100644
--- a/content/browser/storage_partition_impl.cc
+++ b/content/browser/storage_partition_impl.cc
@@ -1285,6 +1285,10 @@ void StoragePartitionImpl::Initialize(
base::BindRepeating(&StoragePartitionImpl::GetQuotaSettings,
weak_factory_.GetWeakPtr()));
quota_manager_ = quota_context_->quota_manager();
+
+ if (config_.quota())
+ quota_manager_->SetQuota(config_.quota().value());
+
scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy =
quota_manager_->proxy();

diff --git a/content/public/browser/storage_partition_config.cc b/content/public/browser/storage_partition_config.cc
index 81013d6eb993a9a1bfbdf0bea388e249c9045c05..55c4770b8933afbc7ae7e2c4dd17f73f193a0225 100644
--- a/content/public/browser/storage_partition_config.cc
+++ b/content/public/browser/storage_partition_config.cc
@@ -21,8 +21,10 @@ StoragePartitionConfig& StoragePartitionConfig::operator=(

// static
StoragePartitionConfig StoragePartitionConfig::CreateDefault(
- BrowserContext* browser_context) {
- return StoragePartitionConfig("", "", browser_context->IsOffTheRecord());
+ BrowserContext* browser_context,
+ absl::optional<int> quota_size) {
+ return StoragePartitionConfig("", "", browser_context->IsOffTheRecord(),
+ quota_size);
}

// static
@@ -30,22 +32,26 @@ StoragePartitionConfig StoragePartitionConfig::Create(
BrowserContext* browser_context,
const std::string& partition_domain,
const std::string& partition_name,
- bool in_memory) {
+ bool in_memory,
+ absl::optional<int> quota_size) {
// If a caller tries to pass an empty partition_domain something is seriously
// wrong or the calling code is not explicitly signalling its desire to create
// a default partition by calling CreateDefault().
CHECK(!partition_domain.empty());
return StoragePartitionConfig(partition_domain, partition_name,
- in_memory || browser_context->IsOffTheRecord());
+ in_memory || browser_context->IsOffTheRecord(),
+ quota_size);
}

StoragePartitionConfig::StoragePartitionConfig(
const std::string& partition_domain,
const std::string& partition_name,
- bool in_memory)
+ bool in_memory,
+ absl::optional<int> quota_size)
: partition_domain_(partition_domain),
partition_name_(partition_name),
- in_memory_(in_memory) {}
+ in_memory_(in_memory),
+ quota_size_(quota_size){}

absl::optional<StoragePartitionConfig>
StoragePartitionConfig::GetFallbackForBlobUrls() const {
@@ -55,7 +61,8 @@ StoragePartitionConfig::GetFallbackForBlobUrls() const {
return StoragePartitionConfig(
partition_domain_, "",
/*in_memory=*/fallback_to_partition_domain_for_blob_urls_ ==
- FallbackMode::kFallbackPartitionInMemory);
+ FallbackMode::kFallbackPartitionInMemory,
+ quota_size_);
}

bool StoragePartitionConfig::operator<(
diff --git a/content/public/browser/storage_partition_config.h b/content/public/browser/storage_partition_config.h
index e765f81673b08f51f6b7c3370c535fae03d41362..786d53df985637560737230d4c1182daf196d3bc 100644
--- a/content/public/browser/storage_partition_config.h
+++ b/content/public/browser/storage_partition_config.h
@@ -28,7 +28,8 @@ class CONTENT_EXPORT StoragePartitionConfig {

// Creates a default config for |browser_context|. If |browser_context| is an
// off-the-record profile, then the config will have |in_memory_| set to true.
- static StoragePartitionConfig CreateDefault(BrowserContext* browser_context);
+ static StoragePartitionConfig CreateDefault(BrowserContext* browser_context,
+ absl::optional<int> quota_size = absl::nullopt);

// Creates a config tied to a specific domain.
// The |partition_domain| is [a-z]* UTF-8 string, specifying the domain in
@@ -43,11 +44,13 @@ class CONTENT_EXPORT StoragePartitionConfig {
static StoragePartitionConfig Create(BrowserContext* browser_context,
const std::string& partition_domain,
const std::string& partition_name,
- bool in_memory);
+ bool in_memory,
+ absl::optional<int> quota_size = absl::nullopt);

std::string partition_domain() const { return partition_domain_; }
std::string partition_name() const { return partition_name_; }
bool in_memory() const { return in_memory_; }
+ absl::optional<int> quota() const { return quota_size_; }

// Returns true if this config was created by CreateDefault() or is
// a copy of a config created with that method.
@@ -94,11 +97,13 @@ class CONTENT_EXPORT StoragePartitionConfig {

StoragePartitionConfig(const std::string& partition_domain,
const std::string& partition_name,
- bool in_memory);
+ bool in_memory,
+ absl::optional<int> quota_size = absl::nullopt);

std::string partition_domain_;
std::string partition_name_;
bool in_memory_ = false;
+ absl::optional<int> quota_size_ = absl::nullopt;
FallbackMode fallback_to_partition_domain_for_blob_urls_ =
FallbackMode::kNone;
};
diff --git a/storage/browser/quota/quota_manager_impl.cc b/storage/browser/quota/quota_manager_impl.cc
index a7cf23f4f4c1876f7b01b6275b218c4622a225e3..1a939a07d8bb205b8063ed8b4fb166672682624a 100644
--- a/storage/browser/quota/quota_manager_impl.cc
+++ b/storage/browser/quota/quota_manager_impl.cc
@@ -2619,7 +2619,7 @@ void QuotaManagerImpl::GetStorageCapacity(StorageCapacityCallback callback) {
db_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&QuotaManagerImpl::CallGetVolumeInfo, get_volume_info_fn_,
- profile_path_),
+ profile_path_,start_quota_),
base::BindOnce(&QuotaManagerImpl::DidGetStorageCapacity,
weak_factory_.GetWeakPtr()));
}
@@ -3038,16 +3038,26 @@ void QuotaManagerImpl::PostTaskAndReplyWithResultForDBThread(
std::move(reply));
}

+
+void QuotaManagerImpl::SetQuota(int start_quota) {
+ // Set the quota for a browser context */
+ // If an electron::BrowserWindow uses a partition path
+ // with no existing browser context, then
+ // this quota takes effect
+ start_quota_ = start_quota;
+}
+
// static
QuotaAvailability QuotaManagerImpl::CallGetVolumeInfo(
GetVolumeInfoFn get_volume_info_fn,
- const base::FilePath& path) {
+ const base::FilePath& path,
+ const absl::optional<int>& quota_size) {
if (!base::CreateDirectory(path)) {
LOG(WARNING) << "Create directory failed for path" << path.value();
return QuotaAvailability(0, 0);
}

- const QuotaAvailability quotaAvailability = get_volume_info_fn(path);
+ const QuotaAvailability quotaAvailability = get_volume_info_fn(path,quota_size);
const auto total = quotaAvailability.total;
const auto available = quotaAvailability.available;

@@ -3069,9 +3079,16 @@ QuotaAvailability QuotaManagerImpl::CallGetVolumeInfo(
}

// static
-QuotaAvailability QuotaManagerImpl::GetVolumeInfo(const base::FilePath& path) {
- return QuotaAvailability(base::SysInfo::AmountOfTotalDiskSpace(path),
- base::SysInfo::AmountOfFreeDiskSpace(path));
+QuotaAvailability QuotaManagerImpl::GetVolumeInfo(
+ const base::FilePath& path,
+ const absl::optional<int>& quota_size) {
+ if (!quota_size) {
+ return QuotaAvailability(base::SysInfo::AmountOfTotalDiskSpace(path),
+ base::SysInfo::AmountOfFreeDiskSpace(path));
+ } else {
+ return QuotaAvailability(base::SysInfo::AmountOfTotalDiskSpace(path),
+ quota_size.value());
+ }
}

void QuotaManagerImpl::AddObserver(
diff --git a/storage/browser/quota/quota_manager_impl.h b/storage/browser/quota/quota_manager_impl.h
index 9598b8674ecf386bfcd0423b51dd8ac3f0d7e379..3f5ef0bd50253ba07cc50ffc6a2378bce88e542b 100644
--- a/storage/browser/quota/quota_manager_impl.h
+++ b/storage/browser/quota/quota_manager_impl.h
@@ -159,7 +159,8 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerImpl
// Function pointer type used to store the function which returns
// information about the volume containing the given FilePath.
// The value returned is the QuotaAvailability struct.
- using GetVolumeInfoFn = QuotaAvailability (*)(const base::FilePath&);
+ using GetVolumeInfoFn = QuotaAvailability (*)(const base::FilePath&,
+ const absl::optional<int>&);

static constexpr int64_t kGBytes = 1024 * 1024 * 1024;
static constexpr int64_t kNoLimit = INT64_MAX;
@@ -468,6 +469,8 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerImpl
eviction_disabled_ = disable;
}

+ void SetQuota(const int start_quota);
+
// Testing support for handling corruption in the underlying database.
//
// Runs `corrupter` on the same sequence used to do database I/O,
@@ -743,8 +746,10 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerImpl
bool is_bootstrap_task = false);

static QuotaAvailability CallGetVolumeInfo(GetVolumeInfoFn get_volume_info_fn,
- const base::FilePath& path);
- static QuotaAvailability GetVolumeInfo(const base::FilePath& path);
+ const base::FilePath& path,
+ const absl::optional<int>& quota_size = absl::nullopt);
+ static QuotaAvailability GetVolumeInfo(const base::FilePath& path,
+ const absl::optional<int>& quota_size = absl::nullopt);

const bool is_incognito_;
const base::FilePath profile_path_;
@@ -838,6 +843,8 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerImpl
// QuotaManagerImpl::GetVolumeInfo.
GetVolumeInfoFn get_volume_info_fn_;

+ absl::optional<int> start_quota_ = absl::nullopt;
+
std::unique_ptr<EvictionRoundInfoHelper> eviction_helper_;
std::map<BucketSetDataDeleter*, std::unique_ptr<BucketSetDataDeleter>>
bucket_set_data_deleters_;
12 changes: 12 additions & 0 deletions shell/browser/electron_browser_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,9 @@ ElectronBrowserContext::ElectronBrowserContext(
&partition_location)) {
const base::FilePath& partition_path = filepath_partition->get();
path_ = std::move(partition_path);
if (auto quota_size_opt = options.FindInt("quota")) {
user_set_quota_ = quota_size_opt.value();
}
}

BrowserContextDependencyManager::GetInstance()->MarkBrowserContextLive(this);
Expand Down Expand Up @@ -173,6 +176,15 @@ ElectronBrowserContext::~ElectronBrowserContext() {
std::move(resource_context_));
}

content::StoragePartition*
ElectronBrowserContext::GetDefaultStoragePartition() {
if (user_set_quota_) {
return GetStoragePartition(
content::StoragePartitionConfig::CreateDefault(this, user_set_quota_));
}
return BrowserContext::GetDefaultStoragePartition();
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about this:

content::StoragePartition* partition = BrowserContext::GetDefaultStoragePartition();
partition->GetQuotaManager()->SetQuotaSettings(storage::GetHardCodedSettings(quota));
return partition;

You can then avoid modifying StoragePartitionConfig.

Copy link
Author

@gecko19 gecko19 Jan 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about this:

content::StoragePartition* partition = BrowserContext::GetDefaultStoragePartition();
partition->GetQuotaManager()->SetQuotaSettings(storage::GetHardCodedSettings(quota));
return partition;

You can then avoid modifying StoragePartitionConfig.

I had a try with these changes. To have SetQuotaSettings() callable, the header file storage/browser/quota/quota_manager.h had to be added.

Since the SetQuotaSettings call can be done only on the IOThread, I tried the snippet below, but then there was a DCHECK fault since the IOthread is not accessible within the electron layer.

 content::StoragePartition* partition = BrowserContext::GetDefaultStoragePartition();
 base::WeakPtrFactory<storage::QuotaManager> quota_weak_factory_{partition->GetQuotaManager()};
 content::GetIOThreadTaskRunner({})->PostTask(
          FROM_HERE, base::BindOnce(&storage::QuotaManager::SetQuotaSettings,
          quota_weak_factory_.GetWeakPtr(),storage::GetHardCodedSettings(quota_size)));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the exact DCHECK error? ElectronBrowserContext::~ElectronBrowserContext() has these lines:
BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, std::move(resource_context_));


void ElectronBrowserContext::InitPrefs() {
auto prefs_path = GetPath().Append(FILE_PATH_LITERAL("Preferences"));
ScopedAllowBlockingForElectron allow_blocking;
Expand Down
2 changes: 2 additions & 0 deletions shell/browser/electron_browser_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ class ElectronBrowserContext : public content::BrowserContext {
std::string GetMediaDeviceIDSalt() override;
content::DownloadManagerDelegate* GetDownloadManagerDelegate() override;
content::BrowserPluginGuestManager* GetGuestManager() override;
content::StoragePartition* GetDefaultStoragePartition();
content::PlatformNotificationService* GetPlatformNotificationService()
override;
content::PermissionControllerDelegate* GetPermissionControllerDelegate()
Expand Down Expand Up @@ -255,6 +256,7 @@ class ElectronBrowserContext : public content::BrowserContext {
base::FilePath path_;
bool in_memory_ = false;
bool use_cache_ = true;
absl::optional<int> user_set_quota_ = absl::nullopt;
int max_cache_size_ = 0;

#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
Expand Down
31 changes: 31 additions & 0 deletions spec/api-session-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,37 @@ describe('session module', () => {
});
});

describe('session.fromPath(path) with a quota.', () => {
const pathloc = 'testsession';
after(() => {
const tmppath = require('electron').app.getPath('temp') + path.sep + pathloc;
if (fs.existsSync(tmppath)) { fs.rmSync(tmppath, { recursive: true, force: true }); }
});
it('Assert that a set quota value is returned when a quota value is specified for a session', async () => {
const quotasize = 256000;
const tmppath = require('electron').app.getPath('temp') + path.sep + pathloc;
if (fs.existsSync(tmppath)) { fs.rmSync(tmppath, { recursive: true, force: true }); }
fs.mkdirSync(tmppath);
const localsession = session.fromPath(tmppath, { quota: quotasize });
const w = new BrowserWindow({
show: false,
webPreferences: {
session: localsession
}
});

const readQuotaSize: any = () => {
return w.webContents.executeJavaScript(`
navigator.storage.estimate().then(estimate => estimate.quota).catch(err => err.message);
`);
};

await w.loadFile(path.join(fixtures, 'api', 'localstorage.html'));
const size = await readQuotaSize();
expect(size).to.equal(quotasize);
});
});

describe('ses.cookies', () => {
const name = '0';
const value = '0';
Expand Down