Selaa lähdekoodia

LibWeb: Add popstate event support

It is going to be useful in writing tests for History API.
Aliaksandr Kalenik 1 vuosi sitten
vanhempi
commit
ee3dd7977d

+ 9 - 0
Tests/LibWeb/Text/data/iframe-popstate-event.html

@@ -0,0 +1,9 @@
+<script>
+    window.history.pushState({}, '', '');
+
+    window.addEventListener('popstate', (e) => {
+        parent.postMessage('popstate event from iframe', '*');
+    });
+
+    window.history.back();
+</script>

+ 1 - 0
Tests/LibWeb/Text/expected/navigation/history-popstate-event.txt

@@ -0,0 +1 @@
+  popstate event from iframe

+ 10 - 0
Tests/LibWeb/Text/input/navigation/history-popstate-event.html

@@ -0,0 +1,10 @@
+<script src="../include.js"></script>
+<iframe src="../../data/iframe-popstate-event.html"></iframe>
+<script>
+    asyncTest(done => {
+        window.addEventListener("message", event => {
+            println(event.data);
+            done();
+        });
+    });
+</script>

+ 1 - 0
Userland/Libraries/LibWeb/CMakeLists.txt

@@ -380,6 +380,7 @@ set(SOURCES
     HTML/Origin.cpp
     HTML/Origin.cpp
     HTML/PageTransitionEvent.cpp
     HTML/PageTransitionEvent.cpp
     HTML/PolicyContainers.cpp
     HTML/PolicyContainers.cpp
+    HTML/PopStateEvent.cpp
     HTML/Parser/Entities.cpp
     HTML/Parser/Entities.cpp
     HTML/Parser/HTMLEncodingDetection.cpp
     HTML/Parser/HTMLEncodingDetection.cpp
     HTML/Parser/HTMLParser.cpp
     HTML/Parser/HTMLParser.cpp

+ 10 - 3
Userland/Libraries/LibWeb/DOM/Document.cpp

@@ -88,6 +88,7 @@
 #include <LibWeb/HTML/Numbers.h>
 #include <LibWeb/HTML/Numbers.h>
 #include <LibWeb/HTML/Origin.h>
 #include <LibWeb/HTML/Origin.h>
 #include <LibWeb/HTML/Parser/HTMLParser.h>
 #include <LibWeb/HTML/Parser/HTMLParser.h>
+#include <LibWeb/HTML/PopStateEvent.h>
 #include <LibWeb/HTML/Scripting/ClassicScript.h>
 #include <LibWeb/HTML/Scripting/ClassicScript.h>
 #include <LibWeb/HTML/Scripting/ExceptionReporter.h>
 #include <LibWeb/HTML/Scripting/ExceptionReporter.h>
 #include <LibWeb/HTML/Scripting/WindowEnvironmentSettingsObject.h>
 #include <LibWeb/HTML/Scripting/WindowEnvironmentSettingsObject.h>
@@ -3993,9 +3994,15 @@ void Document::update_for_history_step_application(JS::NonnullGCPtr<HTML::Sessio
                 navigation->update_the_navigation_api_entries_for_a_same_document_navigation(entry, Bindings::NavigationType::Traverse);
                 navigation->update_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::Window>(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.
             // FIXME: 3. Restore persisted state given entry.
 
 

+ 5 - 0
Userland/Libraries/LibWeb/HTML/History.cpp

@@ -77,6 +77,11 @@ WebIDL::ExceptionOr<JS::Value> History::state() const
     return m_state;
     return m_state;
 }
 }
 
 
+JS::Value History::unsafe_state() const
+{
+    return m_state;
+}
+
 // https://html.spec.whatwg.org/multipage/history.html#dom-history-go
 // https://html.spec.whatwg.org/multipage/history.html#dom-history-go
 WebIDL::ExceptionOr<void> History::go(WebIDL::Long delta = 0)
 WebIDL::ExceptionOr<void> History::go(WebIDL::Long delta = 0)
 {
 {

+ 1 - 0
Userland/Libraries/LibWeb/HTML/History.h

@@ -34,6 +34,7 @@ public:
     u64 m_index { 0 };
     u64 m_index { 0 };
     u64 m_length { 0 };
     u64 m_length { 0 };
 
 
+    JS::Value unsafe_state() const;
     void set_state(JS::Value s) { m_state = s; }
     void set_state(JS::Value s) { m_state = s; }
 
 
 private:
 private:

+ 45 - 0
Userland/Libraries/LibWeb/HTML/PopStateEvent.cpp

@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibJS/Heap/Heap.h>
+#include <LibJS/Runtime/Realm.h>
+#include <LibWeb/Bindings/Intrinsics.h>
+#include <LibWeb/Bindings/PopStateEventPrototype.h>
+#include <LibWeb/HTML/PopStateEvent.h>
+
+namespace Web::HTML {
+
+JS_DEFINE_ALLOCATOR(PopStateEvent);
+
+[[nodiscard]] JS::NonnullGCPtr<PopStateEvent> PopStateEvent::create(JS::Realm& realm, FlyString const& event_name, PopStateEventInit const& event_init)
+{
+    return realm.heap().allocate<PopStateEvent>(realm, realm, event_name, event_init);
+}
+
+JS::NonnullGCPtr<PopStateEvent> PopStateEvent::construct_impl(JS::Realm& realm, FlyString const& event_name, PopStateEventInit const& event_init)
+{
+    return realm.heap().allocate<PopStateEvent>(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);
+}
+
+}

+ 36 - 0
Userland/Libraries/LibWeb/HTML/PopStateEvent.h

@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <LibWeb/DOM/Event.h>
+
+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<PopStateEvent> create(JS::Realm&, FlyString const& event_name, PopStateEventInit const&);
+    [[nodiscard]] static JS::NonnullGCPtr<PopStateEvent> 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() };
+};
+
+}

+ 15 - 0
Userland/Libraries/LibWeb/HTML/PopStateEvent.idl

@@ -0,0 +1,15 @@
+#import <DOM/Event.idl>
+
+// 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;
+};

+ 1 - 0
Userland/Libraries/LibWeb/idl_files.cmake

@@ -197,6 +197,7 @@ libweb_js_bindings(HTML/PageTransitionEvent)
 libweb_js_bindings(HTML/Path2D)
 libweb_js_bindings(HTML/Path2D)
 libweb_js_bindings(HTML/Plugin)
 libweb_js_bindings(HTML/Plugin)
 libweb_js_bindings(HTML/PluginArray)
 libweb_js_bindings(HTML/PluginArray)
+libweb_js_bindings(HTML/PopStateEvent)
 libweb_js_bindings(HTML/PromiseRejectionEvent)
 libweb_js_bindings(HTML/PromiseRejectionEvent)
 libweb_js_bindings(HTML/Storage)
 libweb_js_bindings(HTML/Storage)
 libweb_js_bindings(HTML/SubmitEvent)
 libweb_js_bindings(HTML/SubmitEvent)