diff --git a/Libraries/LibWeb/HTML/Storage.cpp b/Libraries/LibWeb/HTML/Storage.cpp index 33f0f5ac75d..d8c1d60ff8b 100644 --- a/Libraries/LibWeb/HTML/Storage.cpp +++ b/Libraries/LibWeb/HTML/Storage.cpp @@ -1,6 +1,7 @@ /* * Copyright (c) 2022, Andreas Kling * Copyright (c) 2023, Luke Wilde + * Copyright (c) 2024, Shannon Booth * * SPDX-License-Identifier: BSD-2-Clause */ @@ -14,13 +15,14 @@ namespace Web::HTML { GC_DEFINE_ALLOCATOR(Storage); -GC::Ref Storage::create(JS::Realm& realm) +GC::Ref Storage::create(JS::Realm& realm, u64 quota_bytes) { - return realm.create(realm); + return realm.create(realm, quota_bytes); } -Storage::Storage(JS::Realm& realm) +Storage::Storage(JS::Realm& realm, u64 quota_bytes) : Bindings::PlatformObject(realm) + , m_quota_bytes(quota_bytes) { m_legacy_platform_object_flags = LegacyPlatformObjectFlags { .supports_indexed_properties = true, @@ -78,6 +80,8 @@ Optional Storage::get_item(StringView key) const // https://html.spec.whatwg.org/multipage/webstorage.html#dom-storage-setitem WebIDL::ExceptionOr Storage::set_item(String const& key, String const& value) { + auto& realm = this->realm(); + // 1. Let oldValue be null. String old_value; @@ -85,6 +89,7 @@ WebIDL::ExceptionOr Storage::set_item(String const& key, String const& val bool reorder = true; // 3. If this's map[key] exists: + auto new_size = m_stored_bytes; if (auto it = m_map.find(key); it != m_map.end()) { // 1. Set oldValue to this's map[key]. old_value = it->value; @@ -95,12 +100,18 @@ WebIDL::ExceptionOr Storage::set_item(String const& key, String const& val // 3. Set reorder to false. reorder = false; + } else { + new_size += key.bytes().size(); } - // FIXME: 4. If value cannot be stored, then throw a "QuotaExceededError" DOMException exception. + // 4. If value cannot be stored, then throw a "QuotaExceededError" DOMException exception. + new_size += value.bytes().size() - old_value.bytes().size(); + if (new_size > m_quota_bytes) + return WebIDL::QuotaExceededError::create(realm, MUST(String::formatted("Unable to store more than {} bytes in storage"sv, m_quota_bytes))); // 5. Set this's map[key] to value. m_map.set(key, value); + m_stored_bytes = new_size; // 6. If reorder is true, then reorder this. if (reorder) @@ -126,6 +137,7 @@ void Storage::remove_item(StringView key) // 3. Remove this's map[key]. m_map.remove(it); + m_stored_bytes = m_stored_bytes - key.bytes().size() - old_value.bytes().size(); // 4. Reorder this. reorder(); diff --git a/Libraries/LibWeb/HTML/Storage.h b/Libraries/LibWeb/HTML/Storage.h index 33c115d52fb..4dd9a9e18e8 100644 --- a/Libraries/LibWeb/HTML/Storage.h +++ b/Libraries/LibWeb/HTML/Storage.h @@ -18,7 +18,7 @@ class Storage : public Bindings::PlatformObject { GC_DECLARE_ALLOCATOR(Storage); public: - [[nodiscard]] static GC::Ref create(JS::Realm&); + [[nodiscard]] static GC::Ref create(JS::Realm&, u64 quota_bytes); ~Storage(); size_t length() const; @@ -33,7 +33,7 @@ public: void dump() const; private: - explicit Storage(JS::Realm&); + explicit Storage(JS::Realm&, u64 quota_limit); virtual void initialize(JS::Realm&) override; @@ -49,6 +49,8 @@ private: void broadcast(StringView key, StringView old_value, StringView new_value); OrderedHashMap m_map; + u64 m_quota_bytes { 0 }; + u64 m_stored_bytes { 0 }; }; } diff --git a/Libraries/LibWeb/HTML/Window.cpp b/Libraries/LibWeb/HTML/Window.cpp index a5e29eecb02..47628470802 100644 --- a/Libraries/LibWeb/HTML/Window.cpp +++ b/Libraries/LibWeb/HTML/Window.cpp @@ -447,10 +447,13 @@ void Window::fire_a_page_transition_event(FlyString const& event_name, bool pers // https://html.spec.whatwg.org/multipage/webstorage.html#dom-localstorage WebIDL::ExceptionOr> Window::local_storage() { + // See table in: https://storage.spec.whatwg.org/#registered-storage-endpoints + constexpr u64 quota_bytes = 5 * MiB; + // FIXME: Implement according to spec. static HashMap> local_storage_per_origin; auto storage = local_storage_per_origin.ensure(associated_document().origin(), [this]() -> GC::Root { - return Storage::create(realm()); + return Storage::create(realm(), quota_bytes); }); return GC::Ref { *storage }; } @@ -458,10 +461,13 @@ WebIDL::ExceptionOr> Window::local_storage() // https://html.spec.whatwg.org/multipage/webstorage.html#dom-sessionstorage WebIDL::ExceptionOr> Window::session_storage() { + // See table in: https://storage.spec.whatwg.org/#registered-storage-endpoints + constexpr u64 quota_bytes = 5 * MiB; + // FIXME: Implement according to spec. static HashMap> session_storage_per_origin; auto storage = session_storage_per_origin.ensure(associated_document().origin(), [this]() -> GC::Root { - return Storage::create(realm()); + return Storage::create(realm(), quota_bytes); }); return GC::Ref { *storage }; } diff --git a/Tests/LibWeb/Text/expected/wpt-import/webstorage/storage_local_setitem_quotaexceedederr.window.txt b/Tests/LibWeb/Text/expected/wpt-import/webstorage/storage_local_setitem_quotaexceedederr.window.txt new file mode 100644 index 00000000000..4327c6a04e8 --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/webstorage/storage_local_setitem_quotaexceedederr.window.txt @@ -0,0 +1 @@ +QuotaExceededError: Unable to store more than 5242880 bytes in storage diff --git a/Tests/LibWeb/Text/expected/wpt-import/webstorage/storage_session_setitem_quotaexceedederr.window.txt b/Tests/LibWeb/Text/expected/wpt-import/webstorage/storage_session_setitem_quotaexceedederr.window.txt new file mode 100644 index 00000000000..4327c6a04e8 --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/webstorage/storage_session_setitem_quotaexceedederr.window.txt @@ -0,0 +1 @@ +QuotaExceededError: Unable to store more than 5242880 bytes in storage diff --git a/Tests/LibWeb/Text/input/wpt-import/webstorage/storage_local_setitem_quotaexceedederr.window.html b/Tests/LibWeb/Text/input/wpt-import/webstorage/storage_local_setitem_quotaexceedederr.window.html new file mode 100644 index 00000000000..f8050e7da1e --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/webstorage/storage_local_setitem_quotaexceedederr.window.html @@ -0,0 +1,22 @@ + + diff --git a/Tests/LibWeb/Text/input/wpt-import/webstorage/storage_session_setitem_quotaexceedederr.window.html b/Tests/LibWeb/Text/input/wpt-import/webstorage/storage_session_setitem_quotaexceedederr.window.html new file mode 100644 index 00000000000..d220c9b99b5 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/webstorage/storage_session_setitem_quotaexceedederr.window.html @@ -0,0 +1,22 @@ + +