Skip to content

Commit f3ecd23

Browse files
shannonboothawesomekling
authored andcommitted
LibWeb/HTML: Enforce quota limit for local and session storage
Fixes a timeout/crash for the two commited WPT tests.
1 parent e767029 commit f3ecd23

File tree

7 files changed

+74
-8
lines changed

7 files changed

+74
-8
lines changed

Libraries/LibWeb/HTML/Storage.cpp

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/*
22
* Copyright (c) 2022, Andreas Kling <andreas@ladybird.org>
33
* Copyright (c) 2023, Luke Wilde <lukew@serenityos.org>
4+
* Copyright (c) 2024, Shannon Booth <shannon@serenityos.org>
45
*
56
* SPDX-License-Identifier: BSD-2-Clause
67
*/
@@ -14,13 +15,14 @@ namespace Web::HTML {
1415

1516
GC_DEFINE_ALLOCATOR(Storage);
1617

17-
GC::Ref<Storage> Storage::create(JS::Realm& realm)
18+
GC::Ref<Storage> Storage::create(JS::Realm& realm, u64 quota_bytes)
1819
{
19-
return realm.create<Storage>(realm);
20+
return realm.create<Storage>(realm, quota_bytes);
2021
}
2122

22-
Storage::Storage(JS::Realm& realm)
23+
Storage::Storage(JS::Realm& realm, u64 quota_bytes)
2324
: Bindings::PlatformObject(realm)
25+
, m_quota_bytes(quota_bytes)
2426
{
2527
m_legacy_platform_object_flags = LegacyPlatformObjectFlags {
2628
.supports_indexed_properties = true,
@@ -78,13 +80,16 @@ Optional<String> Storage::get_item(StringView key) const
7880
// https://html.spec.whatwg.org/multipage/webstorage.html#dom-storage-setitem
7981
WebIDL::ExceptionOr<void> Storage::set_item(String const& key, String const& value)
8082
{
83+
auto& realm = this->realm();
84+
8185
// 1. Let oldValue be null.
8286
String old_value;
8387

8488
// 2. Let reorder be true.
8589
bool reorder = true;
8690

8791
// 3. If this's map[key] exists:
92+
auto new_size = m_stored_bytes;
8893
if (auto it = m_map.find(key); it != m_map.end()) {
8994
// 1. Set oldValue to this's map[key].
9095
old_value = it->value;
@@ -95,12 +100,18 @@ WebIDL::ExceptionOr<void> Storage::set_item(String const& key, String const& val
95100

96101
// 3. Set reorder to false.
97102
reorder = false;
103+
} else {
104+
new_size += key.bytes().size();
98105
}
99106

100-
// FIXME: 4. If value cannot be stored, then throw a "QuotaExceededError" DOMException exception.
107+
// 4. If value cannot be stored, then throw a "QuotaExceededError" DOMException exception.
108+
new_size += value.bytes().size() - old_value.bytes().size();
109+
if (new_size > m_quota_bytes)
110+
return WebIDL::QuotaExceededError::create(realm, MUST(String::formatted("Unable to store more than {} bytes in storage"sv, m_quota_bytes)));
101111

102112
// 5. Set this's map[key] to value.
103113
m_map.set(key, value);
114+
m_stored_bytes = new_size;
104115

105116
// 6. If reorder is true, then reorder this.
106117
if (reorder)
@@ -126,6 +137,7 @@ void Storage::remove_item(StringView key)
126137

127138
// 3. Remove this's map[key].
128139
m_map.remove(it);
140+
m_stored_bytes = m_stored_bytes - key.bytes().size() - old_value.bytes().size();
129141

130142
// 4. Reorder this.
131143
reorder();

Libraries/LibWeb/HTML/Storage.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class Storage : public Bindings::PlatformObject {
1818
GC_DECLARE_ALLOCATOR(Storage);
1919

2020
public:
21-
[[nodiscard]] static GC::Ref<Storage> create(JS::Realm&);
21+
[[nodiscard]] static GC::Ref<Storage> create(JS::Realm&, u64 quota_bytes);
2222
~Storage();
2323

2424
size_t length() const;
@@ -33,7 +33,7 @@ class Storage : public Bindings::PlatformObject {
3333
void dump() const;
3434

3535
private:
36-
explicit Storage(JS::Realm&);
36+
explicit Storage(JS::Realm&, u64 quota_limit);
3737

3838
virtual void initialize(JS::Realm&) override;
3939

@@ -49,6 +49,8 @@ class Storage : public Bindings::PlatformObject {
4949
void broadcast(StringView key, StringView old_value, StringView new_value);
5050

5151
OrderedHashMap<String, String> m_map;
52+
u64 m_quota_bytes { 0 };
53+
u64 m_stored_bytes { 0 };
5254
};
5355

5456
}

Libraries/LibWeb/HTML/Window.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -447,21 +447,27 @@ void Window::fire_a_page_transition_event(FlyString const& event_name, bool pers
447447
// https://html.spec.whatwg.org/multipage/webstorage.html#dom-localstorage
448448
WebIDL::ExceptionOr<GC::Ref<Storage>> Window::local_storage()
449449
{
450+
// See table in: https://storage.spec.whatwg.org/#registered-storage-endpoints
451+
constexpr u64 quota_bytes = 5 * MiB;
452+
450453
// FIXME: Implement according to spec.
451454
static HashMap<URL::Origin, GC::Root<Storage>> local_storage_per_origin;
452455
auto storage = local_storage_per_origin.ensure(associated_document().origin(), [this]() -> GC::Root<Storage> {
453-
return Storage::create(realm());
456+
return Storage::create(realm(), quota_bytes);
454457
});
455458
return GC::Ref { *storage };
456459
}
457460

458461
// https://html.spec.whatwg.org/multipage/webstorage.html#dom-sessionstorage
459462
WebIDL::ExceptionOr<GC::Ref<Storage>> Window::session_storage()
460463
{
464+
// See table in: https://storage.spec.whatwg.org/#registered-storage-endpoints
465+
constexpr u64 quota_bytes = 5 * MiB;
466+
461467
// FIXME: Implement according to spec.
462468
static HashMap<URL::Origin, GC::Root<Storage>> session_storage_per_origin;
463469
auto storage = session_storage_per_origin.ensure(associated_document().origin(), [this]() -> GC::Root<Storage> {
464-
return Storage::create(realm());
470+
return Storage::create(realm(), quota_bytes);
465471
});
466472
return GC::Ref { *storage };
467473
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
QuotaExceededError: Unable to store more than 5242880 bytes in storage
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
QuotaExceededError: Unable to store more than 5242880 bytes in storage
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<script src="../../include.js"></script>
2+
<script>
3+
// FIXME: This should be an import of WPT from webstorage/storage_local_setitem_quotaexceedederr.window.html
4+
test(() => {
5+
localStorage.clear();
6+
7+
var index = 0;
8+
var key = "name";
9+
var val = "x".repeat(1024);
10+
11+
try {
12+
while (true) {
13+
index++;
14+
localStorage.setItem("" + key + index, "" + val + index);
15+
}
16+
} catch (e) {
17+
println(e);
18+
}
19+
20+
localStorage.clear();
21+
});
22+
</script>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<script src="../../include.js"></script>
2+
<script>
3+
// FIXME: This should be an import of WPT from webstorage/storage_session_setitem_quotaexceedederr.window.html
4+
test(() => {
5+
sessionStorage.clear();
6+
7+
var index = 0;
8+
var key = "name";
9+
var val = "x".repeat(1024);
10+
11+
try {
12+
while (true) {
13+
index++;
14+
sessionStorage.setItem("" + key + index, "" + val + index);
15+
}
16+
} catch (e) {
17+
println(e);
18+
}
19+
20+
sessionStorage.clear();
21+
});
22+
</script>

0 commit comments

Comments
 (0)