diff --git a/Libraries/LibWeb/HTML/TraversableNavigable.cpp b/Libraries/LibWeb/HTML/TraversableNavigable.cpp
index 50466d4af3..64dbad07ce 100644
--- a/Libraries/LibWeb/HTML/TraversableNavigable.cpp
+++ b/Libraries/LibWeb/HTML/TraversableNavigable.cpp
@@ -125,7 +125,11 @@ GC::Ref TraversableNavigable::create_a_new_top_level_trave
traversable->m_session_history_entries.append(*initial_history_entry);
traversable->set_has_session_history_entry_and_ready_for_navigation();
- // FIXME: 10. If opener is non-null, then legacy-clone a traversable storage shed given opener's top-level traversable and traversable. [STORAGE]
+ // 10. If opener is non-null, then legacy-clone a traversable storage shed given opener's top-level traversable and traversable. [STORAGE]
+ if (opener) {
+ auto opener_traversable = opener->top_level_traversable();
+ traversable->storage_shed().legacy_clone(opener_traversable->storage_shed(), page);
+ }
// 11. Append traversable to the user agent's top-level traversable set.
user_agent_top_level_traversable_set().set(traversable);
diff --git a/Libraries/LibWeb/StorageAPI/StorageBottle.cpp b/Libraries/LibWeb/StorageAPI/StorageBottle.cpp
index 644c2b867c..c4189a3c60 100644
--- a/Libraries/LibWeb/StorageAPI/StorageBottle.cpp
+++ b/Libraries/LibWeb/StorageAPI/StorageBottle.cpp
@@ -183,4 +183,9 @@ void SessionStorageBottle::remove(String const& key)
m_map.remove(key);
}
+void SessionStorageBottle::copy_map_from(SessionStorageBottle const& other)
+{
+ m_map = other.m_map;
+}
+
}
diff --git a/Libraries/LibWeb/StorageAPI/StorageBottle.h b/Libraries/LibWeb/StorageAPI/StorageBottle.h
index 9451cf1f14..584b39f3c5 100644
--- a/Libraries/LibWeb/StorageAPI/StorageBottle.h
+++ b/Libraries/LibWeb/StorageAPI/StorageBottle.h
@@ -97,6 +97,8 @@ public:
virtual void clear() override;
virtual void remove(String const&) override;
+ void copy_map_from(SessionStorageBottle const&);
+
private:
explicit SessionStorageBottle(Optional quota)
: StorageBottle(quota)
diff --git a/Libraries/LibWeb/StorageAPI/StorageShed.cpp b/Libraries/LibWeb/StorageAPI/StorageShed.cpp
index 812abfcaad..d923e09997 100644
--- a/Libraries/LibWeb/StorageAPI/StorageShed.cpp
+++ b/Libraries/LibWeb/StorageAPI/StorageShed.cpp
@@ -4,9 +4,11 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
+#include
#include
#include
#include
+#include
#include
namespace Web::StorageAPI {
@@ -38,4 +40,28 @@ GC::Ptr StorageShed::obtain_a_storage_shelf(HTML::EnvironmentSetti
});
}
+// https://storage.spec.whatwg.org/#legacy-clone-a-traversable-storage-shed
+// To legacy-clone a traversable storage shed, given a traversable navigable A and a traversable navigable B, run these steps:
+void StorageShed::legacy_clone(StorageShed const& a_storage_shed, GC::Ref page)
+{
+ // 1. For each key → shelf of A’s storage shed:
+ for (auto const& [key, shelf] : a_storage_shed.m_data) {
+
+ // 1. Let newShelf be the result of running create a storage shelf with "session".
+ auto new_shelf = StorageShelf::create(heap(), page, key, StorageType::Session);
+
+ // 2. Set newShelf’s bucket map["default"]'s bottle map["sessionStorage"]'s map to a clone of shelf’s bucket map["default"]'s bottle map["sessionStorage"]'s map.
+ auto& shelf_bucket = *shelf->bucket_map().get("default"sv).value();
+ auto& new_shelf_bucket = *new_shelf->bucket_map().get("default"sv).value();
+
+ auto const& shelf_bottle = *shelf_bucket.bottle_map()[to_underlying(StorageEndpointType::SessionStorage)];
+ auto& new_shelf_bottle = *new_shelf_bucket.bottle_map()[to_underlying(StorageEndpointType::SessionStorage)];
+
+ as(new_shelf_bottle).copy_map_from(static_cast(shelf_bottle));
+
+ // 3. Set B’s storage shed[key] to newShelf.
+ m_data.set(key, new_shelf);
+ }
+}
+
}
diff --git a/Libraries/LibWeb/StorageAPI/StorageShed.h b/Libraries/LibWeb/StorageAPI/StorageShed.h
index 9bcf643b04..41966960f9 100644
--- a/Libraries/LibWeb/StorageAPI/StorageShed.h
+++ b/Libraries/LibWeb/StorageAPI/StorageShed.h
@@ -25,6 +25,7 @@ public:
static GC::Ref create(GC::Heap& heap) { return heap.allocate(); }
GC::Ptr obtain_a_storage_shelf(HTML::EnvironmentSettingsObject&, StorageType);
+ void legacy_clone(StorageShed const&, GC::Ref);
virtual void visit_edges(GC::Cell::Visitor& visitor) override;
diff --git a/Tests/LibWeb/TestConfig.ini b/Tests/LibWeb/TestConfig.ini
index f257414c04..40bce6b484 100644
--- a/Tests/LibWeb/TestConfig.ini
+++ b/Tests/LibWeb/TestConfig.ini
@@ -32,6 +32,7 @@ Text/input/wpt-import/html/syntax/parsing/unclosed-svg-script.html
; These tests require cross-origin iframe access to succeed.
Text/input/wpt-import/webstorage/event_session_oldvalue.html
Text/input/wpt-import/webstorage/event_session_storagearea.html
+Text/input/wpt-import/webstorage/storage_session_window_open.window.html
Text/input/navigation/navigation-navigate.html
Text/input/navigation/location-reload-fetch.html
Text/input/HTML/session-storage-event-fired-to-lazy-window.html
diff --git a/Tests/LibWeb/Text/expected/wpt-import/webstorage/storage_session_window_open.window.txt b/Tests/LibWeb/Text/expected/wpt-import/webstorage/storage_session_window_open.window.txt
new file mode 100644
index 0000000000..d6a98cbd25
--- /dev/null
+++ b/Tests/LibWeb/Text/expected/wpt-import/webstorage/storage_session_window_open.window.txt
@@ -0,0 +1,6 @@
+Harness status: OK
+
+Found 1 tests
+
+1 Pass
+Pass A new window to make sure there is a copy of the previous window's sessionStorage, and that they diverge after a change
\ No newline at end of file
diff --git a/Tests/LibWeb/Text/input/wpt-import/webstorage/resources/storage_session_window_open_second.html b/Tests/LibWeb/Text/input/wpt-import/webstorage/resources/storage_session_window_open_second.html
new file mode 100644
index 0000000000..2eeff0b865
--- /dev/null
+++ b/Tests/LibWeb/Text/input/wpt-import/webstorage/resources/storage_session_window_open_second.html
@@ -0,0 +1,41 @@
+
+
+
+WebStorage Test: sessionStorage - second page
+
+
+
+
+
diff --git a/Tests/LibWeb/Text/input/wpt-import/webstorage/storage_session_window_open.window.html b/Tests/LibWeb/Text/input/wpt-import/webstorage/storage_session_window_open.window.html
new file mode 100644
index 0000000000..a6d935f5b1
--- /dev/null
+++ b/Tests/LibWeb/Text/input/wpt-import/webstorage/storage_session_window_open.window.html
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/Tests/LibWeb/Text/input/wpt-import/webstorage/storage_session_window_open.window.js b/Tests/LibWeb/Text/input/wpt-import/webstorage/storage_session_window_open.window.js
new file mode 100644
index 0000000000..83d4447017
--- /dev/null
+++ b/Tests/LibWeb/Text/input/wpt-import/webstorage/storage_session_window_open.window.js
@@ -0,0 +1,17 @@
+async_test(function(t) {
+
+ var storage = window.sessionStorage;
+ storage.clear();
+
+ storage.setItem("FOO", "BAR");
+ var win = window.open("resources/storage_session_window_open_second.html");
+ storage.setItem("BAZ", "QUX");
+ window.addEventListener('message', t.step_func(function(e) {
+ e.data.forEach(t.step_func(function(assertion) {
+ assert_equals(assertion.actual, assertion.expected, assertion.message);
+ }));
+ win.close();
+ t.done();
+ }));
+
+}, "A new window to make sure there is a copy of the previous window's sessionStorage, and that they diverge after a change");