diff --git a/Tests/LibWeb/Text/data/iframe-popstate-event.html b/Tests/LibWeb/Text/data/iframe-popstate-event.html
new file mode 100644
index 00000000000..d2ec29fdb6b
--- /dev/null
+++ b/Tests/LibWeb/Text/data/iframe-popstate-event.html
@@ -0,0 +1,9 @@
+
diff --git a/Tests/LibWeb/Text/expected/navigation/history-popstate-event.txt b/Tests/LibWeb/Text/expected/navigation/history-popstate-event.txt
new file mode 100644
index 00000000000..ddb5e9a68b2
--- /dev/null
+++ b/Tests/LibWeb/Text/expected/navigation/history-popstate-event.txt
@@ -0,0 +1 @@
+ popstate event from iframe
diff --git a/Tests/LibWeb/Text/input/navigation/history-popstate-event.html b/Tests/LibWeb/Text/input/navigation/history-popstate-event.html
new file mode 100644
index 00000000000..6337a508fe3
--- /dev/null
+++ b/Tests/LibWeb/Text/input/navigation/history-popstate-event.html
@@ -0,0 +1,10 @@
+
+
+
diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt
index d70a31607e9..c733043f30e 100644
--- a/Userland/Libraries/LibWeb/CMakeLists.txt
+++ b/Userland/Libraries/LibWeb/CMakeLists.txt
@@ -380,6 +380,7 @@ set(SOURCES
HTML/Origin.cpp
HTML/PageTransitionEvent.cpp
HTML/PolicyContainers.cpp
+ HTML/PopStateEvent.cpp
HTML/Parser/Entities.cpp
HTML/Parser/HTMLEncodingDetection.cpp
HTML/Parser/HTMLParser.cpp
diff --git a/Userland/Libraries/LibWeb/DOM/Document.cpp b/Userland/Libraries/LibWeb/DOM/Document.cpp
index 9753f95436f..8141828f527 100644
--- a/Userland/Libraries/LibWeb/DOM/Document.cpp
+++ b/Userland/Libraries/LibWeb/DOM/Document.cpp
@@ -88,6 +88,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -3993,9 +3994,15 @@ void Document::update_for_history_step_application(JS::NonnullGCPtrupdate_the_navigation_api_entries_for_a_same_document_navigation(entry, Bindings::NavigationType::Traverse);
}
- // FIXME: 2. Fire an event named popstate at document's relevant global object, using PopStateEvent,
- // with the state attribute initialized to document's history object's state and hasUAVisualTransition initialized to true
- // if a visual transition, to display a cached rendered state of the latest entry, was done by the user agent.
+ // 2. Fire an event named popstate at document's relevant global object, using PopStateEvent,
+ // with the state attribute initialized to document's history object's state and hasUAVisualTransition initialized to true
+ // if a visual transition, to display a cached rendered state of the latest entry, was done by the user agent.
+ // FIXME: Initialise hasUAVisualTransition
+ HTML::PopStateEventInit popstate_event_init;
+ popstate_event_init.state = history()->unsafe_state();
+ auto& relevant_global_object = verify_cast(HTML::relevant_global_object(*this));
+ auto pop_state_event = HTML::PopStateEvent::create(realm(), "popstate"_fly_string, popstate_event_init);
+ relevant_global_object.dispatch_event(pop_state_event);
// FIXME: 3. Restore persisted state given entry.
diff --git a/Userland/Libraries/LibWeb/HTML/History.cpp b/Userland/Libraries/LibWeb/HTML/History.cpp
index fc6dfbe6528..d3f7f0ee0e9 100644
--- a/Userland/Libraries/LibWeb/HTML/History.cpp
+++ b/Userland/Libraries/LibWeb/HTML/History.cpp
@@ -77,6 +77,11 @@ WebIDL::ExceptionOr History::state() const
return m_state;
}
+JS::Value History::unsafe_state() const
+{
+ return m_state;
+}
+
// https://html.spec.whatwg.org/multipage/history.html#dom-history-go
WebIDL::ExceptionOr History::go(WebIDL::Long delta = 0)
{
diff --git a/Userland/Libraries/LibWeb/HTML/History.h b/Userland/Libraries/LibWeb/HTML/History.h
index b70f6bcd724..26493ba3e4e 100644
--- a/Userland/Libraries/LibWeb/HTML/History.h
+++ b/Userland/Libraries/LibWeb/HTML/History.h
@@ -34,6 +34,7 @@ public:
u64 m_index { 0 };
u64 m_length { 0 };
+ JS::Value unsafe_state() const;
void set_state(JS::Value s) { m_state = s; }
private:
diff --git a/Userland/Libraries/LibWeb/HTML/PopStateEvent.cpp b/Userland/Libraries/LibWeb/HTML/PopStateEvent.cpp
new file mode 100644
index 00000000000..7f5fa7cbf54
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/PopStateEvent.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2024, Aliaksandr Kalenik
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+namespace Web::HTML {
+
+JS_DEFINE_ALLOCATOR(PopStateEvent);
+
+[[nodiscard]] JS::NonnullGCPtr PopStateEvent::create(JS::Realm& realm, FlyString const& event_name, PopStateEventInit const& event_init)
+{
+ return realm.heap().allocate(realm, realm, event_name, event_init);
+}
+
+JS::NonnullGCPtr PopStateEvent::construct_impl(JS::Realm& realm, FlyString const& event_name, PopStateEventInit const& event_init)
+{
+ return realm.heap().allocate(realm, realm, event_name, event_init);
+}
+
+PopStateEvent::PopStateEvent(JS::Realm& realm, FlyString const& event_name, PopStateEventInit const& event_init)
+ : DOM::Event(realm, event_name, event_init)
+ , m_state(event_init.state)
+{
+}
+
+void PopStateEvent::initialize(JS::Realm& realm)
+{
+ Base::initialize(realm);
+ WEB_SET_PROTOTYPE_FOR_INTERFACE(PopStateEvent);
+}
+
+void PopStateEvent::visit_edges(JS::Cell::Visitor& visitor)
+{
+ Base::visit_edges(visitor);
+ visitor.visit(m_state);
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/PopStateEvent.h b/Userland/Libraries/LibWeb/HTML/PopStateEvent.h
new file mode 100644
index 00000000000..379c658331f
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/PopStateEvent.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2024, Aliaksandr Kalenik
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include
+
+namespace Web::HTML {
+
+struct PopStateEventInit : public DOM::EventInit {
+ JS::Value state { JS::js_null() };
+};
+
+class PopStateEvent final : public DOM::Event {
+ WEB_PLATFORM_OBJECT(PopStateEvent, DOM::Event);
+ JS_DECLARE_ALLOCATOR(PopStateEvent);
+
+public:
+ [[nodiscard]] static JS::NonnullGCPtr create(JS::Realm&, FlyString const& event_name, PopStateEventInit const&);
+ [[nodiscard]] static JS::NonnullGCPtr construct_impl(JS::Realm&, FlyString const& event_name, PopStateEventInit const&);
+
+ JS::Value const& state() const { return m_state; }
+
+private:
+ PopStateEvent(JS::Realm&, FlyString const& event_name, PopStateEventInit const& event_init);
+
+ virtual void initialize(JS::Realm&) override;
+ virtual void visit_edges(JS::Cell::Visitor& visitor) override;
+
+ JS::Value m_state { JS::js_null() };
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/PopStateEvent.idl b/Userland/Libraries/LibWeb/HTML/PopStateEvent.idl
new file mode 100644
index 00000000000..6df7c00c244
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/PopStateEvent.idl
@@ -0,0 +1,15 @@
+#import
+
+// https://html.spec.whatwg.org/multipage/nav-history-apis.html#popstateevent
+[Exposed=Window]
+interface PopStateEvent : Event {
+ constructor(DOMString type, optional PopStateEventInit eventInitDict = {});
+
+ readonly attribute any state;
+ // FIXME: readonly attribute boolean hasUAVisualTransition;
+};
+
+dictionary PopStateEventInit : EventInit {
+ any state = null;
+ // FIXME: boolean hasUAVisualTransition = false;
+};
diff --git a/Userland/Libraries/LibWeb/idl_files.cmake b/Userland/Libraries/LibWeb/idl_files.cmake
index cac025a4366..05f2a9dcbfc 100644
--- a/Userland/Libraries/LibWeb/idl_files.cmake
+++ b/Userland/Libraries/LibWeb/idl_files.cmake
@@ -197,6 +197,7 @@ libweb_js_bindings(HTML/PageTransitionEvent)
libweb_js_bindings(HTML/Path2D)
libweb_js_bindings(HTML/Plugin)
libweb_js_bindings(HTML/PluginArray)
+libweb_js_bindings(HTML/PopStateEvent)
libweb_js_bindings(HTML/PromiseRejectionEvent)
libweb_js_bindings(HTML/Storage)
libweb_js_bindings(HTML/SubmitEvent)