LibWeb: Add popstate event support

It is going to be useful in writing tests for History API.
This commit is contained in:
Aliaksandr Kalenik 2024-04-10 21:25:31 -07:00 committed by Andreas Kling
parent 0ffc338406
commit ee3dd7977d
Notes: sideshowbarker 2024-07-17 16:42:19 +09:00
11 changed files with 134 additions and 3 deletions

View file

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

View file

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

View file

@ -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>

View file

@ -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

View file

@ -88,6 +88,7 @@
#include <LibWeb/HTML/Numbers.h>
#include <LibWeb/HTML/Origin.h>
#include <LibWeb/HTML/Parser/HTMLParser.h>
#include <LibWeb/HTML/PopStateEvent.h>
#include <LibWeb/HTML/Scripting/ClassicScript.h>
#include <LibWeb/HTML/Scripting/ExceptionReporter.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);
}
// 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.

View file

@ -77,6 +77,11 @@ WebIDL::ExceptionOr<JS::Value> 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<void> History::go(WebIDL::Long delta = 0)
{

View file

@ -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:

View file

@ -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);
}
}

View file

@ -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() };
};
}

View file

@ -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;
};

View file

@ -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)