From 43feec65be78cf8a2d64139f7cd4f977d2ea6c4d Mon Sep 17 00:00:00 2001 From: ldm0 Date: Sat, 31 Oct 2026 20:40:48 +0800 Subject: [PATCH] fix(storage): exclude Web Storage from quota-managed usage Align StorageManager.estimate and CDP Storage.getUsageAndQuota with Chromium's quota clients. Report IndexedDB, Cache Storage, and OPFS by backend, including named and implicit-default bucket data, without touching local/sessionStorage. Keep Web Storage's independent per-area quota unchanged. Cover Web Storage exclusion, an unopened or closed SQLite localStorage backend, default and named OPFS data, bucket attribution, and command-session isolation. --- moli-core/src/storage.rs | 2 +- .../src/conn/state/browser_context.rs | 71 +++++----- .../src/conn/tests/resource_runtime.rs | 8 +- moli-protocol/src/domains/storage/ops.rs | 12 +- moli-protocol/src/domains/storage/tests.rs | 123 +++++++++++++----- .../window_runtime/navigator.rs | 54 +------- .../misc/extracted/popup_window.rs | 27 +++- .../src/script_vm/tests/indexed_db.rs | 2 +- .../src/script_vm/tests/sqlite_web_storage.rs | 31 +++++ 9 files changed, 207 insertions(+), 123 deletions(-) diff --git a/moli-core/src/storage.rs b/moli-core/src/storage.rs index 265999f6f1..67e532caa8 100644 --- a/moli-core/src/storage.rs +++ b/moli-core/src/storage.rs @@ -6,7 +6,7 @@ pub use moli_renderer_v8::{ }; pub use moli_storage_service::{ DEFAULT_ORIGIN_STORAGE_QUOTA_BYTES, SharedStorageBucketStore, StorageBucketIdentity, - complete_storage_bucket_deletion, new_shared_json_storage_bucket_store, + StorageBucketLocator, complete_storage_bucket_deletion, new_shared_json_storage_bucket_store, new_shared_json_storage_bucket_store_with_cache_root, new_shared_json_storage_bucket_store_with_cache_root_and_indexed_db_manager, new_shared_storage_bucket_store, new_shared_storage_bucket_store_with_indexed_db_manager, diff --git a/moli-protocol/src/conn/state/browser_context.rs b/moli-protocol/src/conn/state/browser_context.rs index 82858a9500..e2be4c30ac 100644 --- a/moli-protocol/src/conn/state/browser_context.rs +++ b/moli-protocol/src/conn/state/browser_context.rs @@ -136,9 +136,9 @@ pub(crate) struct BrowserContextPageStorageHandles { #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub(crate) struct BrowserContextOriginStorageUsage { - pub(crate) local_storage_usage: u64, pub(crate) indexed_db_usage: u64, - pub(crate) storage_buckets_usage: u64, + pub(crate) cache_storage_usage: u64, + pub(crate) file_system_usage: u64, pub(crate) total_usage: u64, } @@ -367,10 +367,6 @@ impl std::fmt::Debug for BrowserContext { } } -fn usize_to_u64_saturating(value: usize) -> u64 { - u64::try_from(value).unwrap_or(u64::MAX) -} - impl BrowserContext { pub(crate) fn active_page_target(&self) -> &PageTargetHost { self.page_targets @@ -771,52 +767,59 @@ impl BrowserContext { &self, serialized_origin: &str, ) -> Result { - let local_storage_usage = { - let store = self.storage_partition.web_storage_store(); - usize_to_u64_saturating( - store - .try_usage_bytes_for_origin_areas(serialized_origin) - .map_err(|error| error.to_string())?, - ) - }; - let indexed_db_usage = moli_core::storage::indexed_db_origins_with_prefix_usage_bytes( + // Web Storage has its own per-area limit and is not a QuotaManager client. + let mut indexed_db_usage = moli_core::storage::indexed_db_origins_with_prefix_usage_bytes( self.storage_partition.indexed_db_manager(), &moli_storage_key::storage_key_prefix_for_origin(serialized_origin), )?; - let storage_buckets_usage = self.storage_bucket_usage_for_origin(serialized_origin)?; - Ok(BrowserContextOriginStorageUsage { - local_storage_usage, - indexed_db_usage, - storage_buckets_usage, - total_usage: local_storage_usage - .saturating_add(indexed_db_usage) - .saturating_add(storage_buckets_usage), - }) - } - - fn storage_bucket_usage_for_origin(&self, serialized_origin: &str) -> Result { - let (bucket_identities, cache_usage, storage_service) = { + let (bucket_identities, default_keys, cache_storage_usage, storage_service) = { let store = self.storage_partition.storage_bucket_store().lock(); ( store.bucket_identities_for_origin_areas(serialized_origin), + store.keys_for_origin_areas(serialized_origin), store.cache_usage_for_origin_areas(serialized_origin), store.storage_service(), ) }; - let mut usage = cache_usage; + let mut default_keys = default_keys + .into_iter() + .map(|(key, _)| key) + .collect::>(); + // OPFS can use the implicit default bucket before any CacheStorage or + // named-bucket metadata exists. Do not make its usage depend on that registry. + let url = url::Url::parse(serialized_origin).map_err(|error| error.to_string())?; + default_keys.insert( + moli_storage_key::MoliStorageKey::first_party_from_url(&url, None) + .serialized_storage_key(), + ); + let mut file_system_usage = 0u64; + for key in default_keys { + let locator = moli_core::storage::StorageBucketLocator::default_bucket(key); + file_system_usage = file_system_usage.saturating_add( + storage_service + .opfs_usage(&locator) + .map_err(|error| format!("FailedToReadStorageBucketOpfsUsage: {error}"))?, + ); + } for identity in bucket_identities { - let indexed_db_usage = moli_core::storage::indexed_db_origin_usage_bytes( + let bucket_indexed_db_usage = moli_core::storage::indexed_db_origin_usage_bytes( self.storage_partition.indexed_db_manager(), &identity.indexed_db_storage_key(), )?; let opfs_usage = storage_service .opfs_usage(&identity.locator()) .map_err(|error| format!("FailedToReadStorageBucketOpfsUsage: {error}"))?; - usage = usage - .saturating_add(indexed_db_usage) - .saturating_add(opfs_usage); + indexed_db_usage = indexed_db_usage.saturating_add(bucket_indexed_db_usage); + file_system_usage = file_system_usage.saturating_add(opfs_usage); } - Ok(usage) + Ok(BrowserContextOriginStorageUsage { + indexed_db_usage, + cache_storage_usage, + file_system_usage, + total_usage: indexed_db_usage + .saturating_add(cache_storage_usage) + .saturating_add(file_system_usage), + }) } fn complete_storage_bucket_deletions( diff --git a/moli-protocol/src/conn/tests/resource_runtime.rs b/moli-protocol/src/conn/tests/resource_runtime.rs index 9447a346bb..f512b67725 100644 --- a/moli-protocol/src/conn/tests/resource_runtime.rs +++ b/moli-protocol/src/conn/tests/resource_runtime.rs @@ -450,7 +450,7 @@ fn browser_context_cookie_snapshot_and_delete_use_storage_partition_cookie_handl } #[test] -fn browser_context_storage_usage_reads_storage_partition_owner() { +fn browser_context_quota_usage_excludes_web_storage() { let browser_context = BrowserContext::new("BID-storage-usage".to_owned()); let origin = Url::parse("https://usage.example/app") .unwrap() @@ -468,10 +468,10 @@ fn browser_context_storage_usage_reads_storage_partition_owner() { .storage_usage_for_origin(&origin) .expect("storage usage should be readable"); - assert_eq!(usage.local_storage_usage, "owner".len() as u64); assert_eq!(usage.indexed_db_usage, 0); - assert_eq!(usage.storage_buckets_usage, 0); - assert_eq!(usage.total_usage, usage.local_storage_usage); + assert_eq!(usage.cache_storage_usage, 0); + assert_eq!(usage.file_system_usage, 0); + assert_eq!(usage.total_usage, 0); } #[test] diff --git a/moli-protocol/src/domains/storage/ops.rs b/moli-protocol/src/domains/storage/ops.rs index 2300b6694f..4ce9b7964a 100644 --- a/moli-protocol/src/domains/storage/ops.rs +++ b/moli-protocol/src/domains/storage/ops.rs @@ -422,16 +422,20 @@ fn get_usage_and_quota_command_output_plan( "overrideActive": override_active, "usageBreakdown": [ { - "storageType": "local_storage", - "usage": usage.local_storage_usage as f64, + "storageType": "file_systems", + "usage": usage.file_system_usage as f64, }, { "storageType": "indexeddb", "usage": usage.indexed_db_usage as f64, }, { - "storageType": "storage_buckets", - "usage": usage.storage_buckets_usage as f64, + "storageType": "cache_storage", + "usage": usage.cache_storage_usage as f64, + }, + { + "storageType": "service_workers", + "usage": 0, } ] })) diff --git a/moli-protocol/src/domains/storage/tests.rs b/moli-protocol/src/domains/storage/tests.rs index 2b374dbba9..e7166798de 100644 --- a/moli-protocol/src/domains/storage/tests.rs +++ b/moli-protocol/src/domains/storage/tests.rs @@ -2510,7 +2510,7 @@ async fn storage_clear_data_for_partitioned_storage_key_clears_exact_partition_o } #[tokio::test(flavor = "multi_thread")] -async fn storage_get_usage_and_quota_reports_local_storage_usage_for_origin() { +async fn storage_get_usage_and_quota_excludes_web_storage_for_origin() { let mut ctx = TestContext::new(); { let fixture_value = Some(BrowserContext::new_with_page_for_test( @@ -2559,22 +2559,29 @@ async fn storage_get_usage_and_quota_reports_local_storage_usage_for_origin() { .await; let result = take_result_by_id(&mut ctx, 12_528); - assert_eq!(json_number_as_u64(&result["usage"]), 5); + assert_eq!(json_number_as_u64(&result["usage"]), 0); assert_eq!( json_number_as_u64(&result["quota"]), moli_core::storage::DEFAULT_ORIGIN_STORAGE_QUOTA_BYTES ); assert_eq!(result["overrideActive"], json!(false)); - assert_eq!(usage_breakdown_value(&result, "local_storage"), 5); assert_eq!(usage_breakdown_value(&result, "indexeddb"), 0); - assert_eq!(usage_breakdown_value(&result, "storage_buckets"), 0); - assert!( + assert_eq!(usage_breakdown_value(&result, "cache_storage"), 0); + assert_eq!(usage_breakdown_value(&result, "file_systems"), 0); + assert_eq!(usage_breakdown_value(&result, "service_workers"), 0); + assert_eq!( result["usageBreakdown"] .as_array() .unwrap() .iter() - .all(|entry| entry["storageType"].as_str() != Some("session_storage")), - "CDP StorageType has no session_storage token" + .map(|entry| entry["storageType"].as_str().unwrap()) + .collect::>(), + [ + "file_systems", + "indexeddb", + "cache_storage", + "service_workers" + ] ); } @@ -2649,7 +2656,8 @@ async fn storage_get_usage_and_quota_reports_indexed_db_usage_for_origin() { indexed_db_usage > 0, "IndexedDB metadata and record bytes should be reported" ); - assert_eq!(usage_breakdown_value(&result, "local_storage"), 0); + assert_eq!(usage_breakdown_value(&result, "cache_storage"), 0); + assert_eq!(usage_breakdown_value(&result, "file_systems"), 0); assert_eq!(json_number_as_u64(&result["usage"]), indexed_db_usage); } @@ -2764,21 +2772,28 @@ async fn storage_get_usage_and_quota_reports_storage_bucket_usage_for_origin() { }; let opfs_usage = { let storage_service = storage_bucket_store.lock().storage_service(); - let locator = bucket_a_identity.locator(); - let bucket_key = moli_storage_service::StorageService::opfs_bucket_key(&locator) - .expect("bucket OPFS key should derive"); - let root = storage_service - .ensure_opfs_root(&locator) - .expect("bucket OPFS root should open"); - let file = storage_service - .with_opfs(|opfs| opfs.get_file(&bucket_key, &root, "usage.txt", true)) - .expect("bucket OPFS file should open"); - storage_service - .with_opfs(|opfs| opfs.write_file(&bucket_key, &file, b"opfs usage", None)) - .expect("bucket OPFS file should write"); - storage_service - .opfs_usage(&locator) - .expect("bucket OPFS usage should load") + [ + bucket_a_identity.locator(), + moli_storage_service::StorageBucketLocator::default_bucket(&storage_key), + ] + .iter() + .map(|locator| { + let bucket_key = moli_storage_service::StorageService::opfs_bucket_key(locator) + .expect("bucket OPFS key should derive"); + let root = storage_service + .ensure_opfs_root(locator) + .expect("bucket OPFS root should open"); + let file = storage_service + .with_opfs(|opfs| opfs.get_file(&bucket_key, &root, "usage.txt", true)) + .expect("bucket OPFS file should open"); + storage_service + .with_opfs(|opfs| opfs.write_file(&bucket_key, &file, b"opfs usage", None)) + .expect("bucket OPFS file should write"); + storage_service + .opfs_usage(locator) + .expect("bucket OPFS usage should load") + }) + .sum::() }; { @@ -2801,7 +2816,6 @@ async fn storage_get_usage_and_quota_reports_storage_bucket_usage_for_origin() { .await; let result = take_result_by_id(&mut ctx, 12_533); - let bucket_usage = usage_breakdown_value(&result, "storage_buckets"); let (bucket_a_usage, bucket_b_usage) = ctx.with_fixture(|conn| { let bc = conn.browser_context.as_ref().unwrap(); let bucket_a_usage = moli_core::storage::indexed_db_origin_usage_bytes( @@ -2817,15 +2831,59 @@ async fn storage_get_usage_and_quota_reports_storage_bucket_usage_for_origin() { (bucket_a_usage, bucket_b_usage) }); assert_eq!( - bucket_usage, + json_number_as_u64(&result["usage"]), bucket_a_usage .saturating_add(bucket_b_usage) .saturating_add(cache_usage) .saturating_add(opfs_usage) ); - assert_eq!(usage_breakdown_value(&result, "local_storage"), 0); + assert_eq!( + usage_breakdown_value(&result, "indexeddb"), + bucket_a_usage.saturating_add(bucket_b_usage) + ); + assert_eq!(usage_breakdown_value(&result, "cache_storage"), cache_usage); + assert_eq!(usage_breakdown_value(&result, "file_systems"), opfs_usage); +} + +#[tokio::test(flavor = "multi_thread")] +async fn storage_get_usage_and_quota_reports_default_opfs_without_bucket_registry_entries() { + let mut ctx = TestContext::new(); + let origin = "https://opfs-only-usage.test"; + let key = first_party_storage_key_for_origin(origin); + let expected = ctx.with_fixture(|conn| { + conn.browser_context = Some(BrowserContext::new("BID-usage-opfs-only".into())); + let store = conn + .browser_context + .as_ref() + .unwrap() + .storage_bucket_store_for_test() + .lock(); + assert!(store.keys_for_origin_areas(origin).is_empty()); + let service = store.storage_service(); + drop(store); + let locator = moli_core::storage::StorageBucketLocator::default_bucket(key); + let bucket_key = moli_storage_service::StorageService::opfs_bucket_key(&locator).unwrap(); + let root = service.ensure_opfs_root(&locator).unwrap(); + let file = service + .with_opfs(|opfs| opfs.get_file(&bucket_key, &root, "only-opfs.txt", true)) + .unwrap(); + service + .with_opfs(|opfs| opfs.write_file(&bucket_key, &file, b"quota-managed bytes", None)) + .unwrap(); + service.opfs_usage(&locator).unwrap() + }); + assert!(expected > 0); + ctx.process_async(json!({ + "id": 12_534, + "method": "Storage.getUsageAndQuota", + "params": {"origin": origin} + })) + .await; + let result = take_result_by_id(&mut ctx, 12_534); + assert_eq!(usage_breakdown_value(&result, "file_systems"), expected); + assert_eq!(usage_breakdown_value(&result, "cache_storage"), 0); assert_eq!(usage_breakdown_value(&result, "indexeddb"), 0); - assert_eq!(json_number_as_u64(&result["usage"]), bucket_usage); + assert_eq!(json_number_as_u64(&result["usage"]), expected); } #[tokio::test(flavor = "multi_thread")] @@ -2851,6 +2909,13 @@ async fn storage_get_usage_and_quota_targets_command_session_browser_context() { let store = inactive.web_storage_store_for_test(); assert!(store.set_item(&storage_key, "local", "bbbb")); } + seed_indexed_db_usage(inactive.indexed_db_manager_for_test(), &storage_key); + let expected_usage = moli_core::storage::indexed_db_origin_usage_bytes( + inactive.indexed_db_manager_for_test(), + &storage_key, + ) + .expect("inactive context IndexedDB usage"); + assert!(expected_usage > 0); ctx.with_fixture(|conn| conn.install_browser_context_fixture_for_test(active)); ctx.with_fixture(|conn| conn.push_inactive_browser_context_fixture_for_test(inactive)); @@ -2870,8 +2935,8 @@ async fn storage_get_usage_and_quota_targets_command_session_browser_context() { let result = response .get("result") .expect("response should include result"); - assert_eq!(json_number_as_u64(&result["usage"]), 4); - assert_eq!(usage_breakdown_value(result, "local_storage"), 4); + assert_eq!(json_number_as_u64(&result["usage"]), expected_usage); + assert_eq!(usage_breakdown_value(result, "indexeddb"), expected_usage); } #[tokio::test(flavor = "multi_thread")] diff --git a/moli-renderer-v8/src/context_bootstrap/window_runtime/navigator.rs b/moli-renderer-v8/src/context_bootstrap/window_runtime/navigator.rs index 27c374fdf5..621cd289d1 100644 --- a/moli-renderer-v8/src/context_bootstrap/window_runtime/navigator.rs +++ b/moli-renderer-v8/src/context_bootstrap/window_runtime/navigator.rs @@ -483,9 +483,6 @@ enum StorageBucketCachedResponseMaterialization<'scope> { struct StorageManagerOwnerContext { storage_key: String, - area_key: Option, - host_ptr: Option<*mut crate::native_bridge::JsContextHost>, - popup_id: Option, } #[derive(webidl::WebIdlArgs)] @@ -706,18 +703,7 @@ pub(in crate::context_bootstrap) fn navigator_storage_estimate_callback<'s>( else { return; }; - let usage = match storage_manager_usage_bytes(scope, &context) { - Ok(usage) => usage, - Err(error) => { - let error = crate::context_bootstrap::new_dom_exception_value( - scope, - &error.to_string(), - "UnknownError", - ); - let _ = resolver.reject(scope, error); - return; - } - }; + let usage = storage_manager_usage_bytes(scope, &context); let estimate = build_storage_estimate_object_with_quota( scope, usage.total, @@ -830,9 +816,6 @@ fn storage_manager_owner_context<'s>( let storage_context = host.storage_context_for_lightweight_popup(popup_id)?; return Some(StorageManagerOwnerContext { storage_key: storage_context.storage_key().serialized_storage_key(), - area_key: Some(storage_context.web_storage_area_key().to_owned()), - host_ptr: Some(host_ptr), - popup_id: Some(popup_id), }); } else { host.top_document_storage_context() @@ -840,19 +823,11 @@ fn storage_manager_owner_context<'s>( }; return Some(StorageManagerOwnerContext { storage_key: context.storage_key().serialized_storage_key(), - area_key: Some(context.web_storage_area_key().to_owned()), - host_ptr: Some(host_ptr), - popup_id: None, }); } let storage_key = crate::worker::worker_storage_key(scope)?; let storage_key = storage_key.serialized_storage_key(); - Some(StorageManagerOwnerContext { - storage_key, - area_key: None, - host_ptr: None, - popup_id: None, - }) + Some(StorageManagerOwnerContext { storage_key }) } fn storage_manager_child_handle<'s>( @@ -875,23 +850,7 @@ fn storage_manager_popup_id<'s>( fn storage_manager_usage_bytes( scope: &mut v8::PinScope<'_, '_>, context: &StorageManagerOwnerContext, -) -> Result { - let web_storage_usage = - if let Some((host_ptr, area_key)) = context.host_ptr.zip(context.area_key.as_deref()) { - let host = unsafe { &*host_ptr }; - let local = host.web_storage_store().try_usage_bytes(area_key)?; - let session = if let Some(popup_id) = context.popup_id { - host.lightweight_popup_session_storage_store(popup_id) - .map(|store| store.try_usage_bytes(area_key)) - .transpose()? - .unwrap_or(0) - } else { - host.session_storage_store().try_usage_bytes(area_key)? - }; - local.saturating_add(session) as u64 - } else { - 0 - }; +) -> StorageUsageSnapshot { let locator = moli_storage_service::StorageBucketLocator::default_bucket(&context.storage_key); let bucket_usage = storage_bucket_quota_owner_for_locator(scope, &locator) .and_then(|owner| owner.usage_snapshot().ok()); @@ -912,16 +871,15 @@ fn storage_manager_usage_bytes( 0, ) }); - Ok(StorageUsageSnapshot { - total: web_storage_usage - .saturating_add(indexed_db_usage) + StorageUsageSnapshot { + total: indexed_db_usage .saturating_add(cache_storage_usage) .saturating_add(opfs_usage), quota, indexed_db: indexed_db_usage, cache_storage: cache_storage_usage, opfs: opfs_usage, - }) + } } pub(in crate::context_bootstrap) fn storage_bucket_manager_open_callback<'s>( diff --git a/moli-renderer-v8/src/script_vm/tests/browser_api/misc/extracted/popup_window.rs b/moli-renderer-v8/src/script_vm/tests/browser_api/misc/extracted/popup_window.rs index c480df763d..79a541132f 100644 --- a/moli-renderer-v8/src/script_vm/tests/browser_api/misc/extracted/popup_window.rs +++ b/moli-renderer-v8/src/script_vm/tests/browser_api/misc/extracted/popup_window.rs @@ -140,6 +140,29 @@ async fn retained_popup_storage_managers_use_bound_popup_owner() { "https://popup-storage-owner.test/page.html", &loader, ); + // Seed quota-managed bytes for the popup's origin independently of the + // retained JS receiver. Reading through the opener's owner would report 0. + { + let host = vm.context_host_weak_for_test().upgrade().unwrap(); + let store = host.borrow().storage_bucket_store(); + let service = store.lock().storage_service(); + let key = moli_storage_key::MoliStorageKey::first_party_from_url( + &url::Url::parse("https://popup-storage-owner-child.test").unwrap(), + None, + ) + .serialized_storage_key(); + let locator = moli_storage_service::StorageBucketLocator::default_bucket(key); + let bucket_key = moli_storage_service::StorageService::opfs_bucket_key(&locator).unwrap(); + let root = service.ensure_opfs_root(&locator).unwrap(); + let file = service + .with_opfs(|opfs| opfs.get_file(&bucket_key, &root, "popup-usage.txt", true)) + .unwrap(); + service + .with_opfs(|opfs| { + opfs.write_file(&bucket_key, &file, b"popup quota-managed data", None) + }) + .unwrap(); + } vm.exec( r#" @@ -647,7 +670,7 @@ fn window_open_lightweight_popup_clones_and_isolates_session_storage() { ); } #[tokio::test] -async fn storage_manager_estimate_uses_lightweight_popup_session_storage_owner() { +async fn storage_manager_estimate_excludes_lightweight_popup_session_storage() { let loader = ResourceRequestClient::new(&moli_fetch::FetchConfig::default()).expect("loader"); let mut vm = new_page_task_executor_test_vm_with_loader("https://example.com/", &loader); @@ -698,7 +721,7 @@ async fn storage_manager_estimate_uses_lightweight_popup_session_storage_owner() assert_eq!( vm.eval("__popupStorageEstimateMessages[0]") .expect("popup StorageManager estimate message should evaluate"), - r#"{"usage":16,"indexedDB":null,"topValue":"AAAAA","popupValue":"BBBBBBBBBBB"}"# + r#"{"usage":0,"indexedDB":null,"topValue":"AAAAA","popupValue":"BBBBBBBBBBB"}"# ); } #[test] diff --git a/moli-renderer-v8/src/script_vm/tests/indexed_db.rs b/moli-renderer-v8/src/script_vm/tests/indexed_db.rs index a5d315cce5..5f05341b41 100644 --- a/moli-renderer-v8/src/script_vm/tests/indexed_db.rs +++ b/moli-renderer-v8/src/script_vm/tests/indexed_db.rs @@ -2740,7 +2740,7 @@ fn navigator_storage_estimate_usage_tracks_current_origin_storage() { assert_eq!( result, - "0||7||true|number|true|true|true|true|true|true|indexedDB,caches,fileSystem|1073741824" + "0||0||true|number|true|true|true|true|true|true|indexedDB,caches,fileSystem|1073741824" ); let other_page_url = format!("https://storage-estimate-other-{nonce}.test/"); diff --git a/moli-renderer-v8/src/script_vm/tests/sqlite_web_storage.rs b/moli-renderer-v8/src/script_vm/tests/sqlite_web_storage.rs index 5adac10353..477a9b4d8b 100644 --- a/moli-renderer-v8/src/script_vm/tests/sqlite_web_storage.rs +++ b/moli-renderer-v8/src/script_vm/tests/sqlite_web_storage.rs @@ -1,5 +1,36 @@ use super::*; +#[test] +fn storage_estimate_does_not_open_local_storage_or_depend_on_its_connection() { + for closed in [false, true] { + let directory = tempfile::tempdir().unwrap(); + let path = directory.path().join("unused/localstorage.sqlite"); + let runtime = moli_sqlite::Runtime::new().unwrap(); + let (local, owner) = moli_web_storage::new_lazy_sqlite_web_storage_store( + path.clone(), + moli_sqlite::OpenMode::Create, + runtime.handle(), + ) + .unwrap(); + let handles = + crate::RendererWebStorageHandles::new(local, crate::new_shared_web_storage_store()); + let mut vm = + new_storage_page_task_executor_test_vm("https://quota-without-webstorage.test/"); + vm.set_web_storage_handles(&handles); + if closed { + owner.close().unwrap(); + } + vm.eval("globalThis.estimateResult = 'pending'; navigator.storage.estimate().then(value => {estimateResult = JSON.stringify([value.usage, typeof value.quota]);}, error => {estimateResult = error.name;});").unwrap(); + assert_eq!( + vm.eval_after_selected_page_tasks("estimateResult").unwrap(), + "[0,\"number\"]" + ); + assert!(!path.parent().unwrap().exists()); + drop(vm); + owner.close().unwrap(); + } +} + #[test] fn sqlite_storage_wrappers_share_utf16_data_without_sharing_session_storage() { let directory = tempfile::tempdir().unwrap();