From ee3dd7977d4c88c836bfb813adcf3e006da749a8 Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Wed, 10 Apr 2024 21:25:31 -0700 Subject: [PATCH] LibWeb: Add popstate event support It is going to be useful in writing tests for History API. --- .../Text/data/iframe-popstate-event.html | 9 ++++ .../navigation/history-popstate-event.txt | 1 + .../navigation/history-popstate-event.html | 10 +++++ Userland/Libraries/LibWeb/CMakeLists.txt | 1 + Userland/Libraries/LibWeb/DOM/Document.cpp | 13 ++++-- Userland/Libraries/LibWeb/HTML/History.cpp | 5 +++ Userland/Libraries/LibWeb/HTML/History.h | 1 + .../Libraries/LibWeb/HTML/PopStateEvent.cpp | 45 +++++++++++++++++++ .../Libraries/LibWeb/HTML/PopStateEvent.h | 36 +++++++++++++++ .../Libraries/LibWeb/HTML/PopStateEvent.idl | 15 +++++++ Userland/Libraries/LibWeb/idl_files.cmake | 1 + 11 files changed, 134 insertions(+), 3 deletions(-) create mode 100644 Tests/LibWeb/Text/data/iframe-popstate-event.html create mode 100644 Tests/LibWeb/Text/expected/navigation/history-popstate-event.txt create mode 100644 Tests/LibWeb/Text/input/navigation/history-popstate-event.html create mode 100644 Userland/Libraries/LibWeb/HTML/PopStateEvent.cpp create mode 100644 Userland/Libraries/LibWeb/HTML/PopStateEvent.h create mode 100644 Userland/Libraries/LibWeb/HTML/PopStateEvent.idl 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)