Forráskód Böngészése

LibWeb: Add hashchange event support

Aliaksandr Kalenik 1 éve
szülő
commit
ccb363c443

+ 1 - 0
Meta/gn/secondary/Userland/Libraries/LibWeb/HTML/BUILD.gn

@@ -113,6 +113,7 @@ source_set("HTML") {
     "HTMLUListElement.cpp",
     "HTMLUnknownElement.cpp",
     "HTMLVideoElement.cpp",
+    "HashChangeEvent.cpp",
     "History.cpp",
     "ImageBitmap.cpp",
     "ImageData.cpp",

+ 1 - 0
Meta/gn/secondary/Userland/Libraries/LibWeb/idl_files.gni

@@ -117,6 +117,7 @@ standard_idl_files = [
   "//Userland/Libraries/LibWeb/HTML/DataTransfer.idl",
   "//Userland/Libraries/LibWeb/HTML/ErrorEvent.idl",
   "//Userland/Libraries/LibWeb/HTML/FormDataEvent.idl",
+  "//Userland/Libraries/LibWeb/HTML/HashChangeEvent.idl",
   "//Userland/Libraries/LibWeb/HTML/History.idl",
   "//Userland/Libraries/LibWeb/HTML/HTMLAllCollection.idl",
   "//Userland/Libraries/LibWeb/HTML/HTMLAnchorElement.idl",

+ 12 - 0
Tests/LibWeb/Text/data/iframe-hashchange-event.html

@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<script>
+    window.addEventListener('hashchange', (event) => {
+        const oldURL = new URL(event.oldURL);
+        const newURL = new URL(event.newURL);
+        parent.postMessage(`hashchange oldURL.hash=${oldURL.hash} newURL.hash=${newURL.hash}`, "*");
+        if (location.hash === "#test")
+            location.hash = "#done";
+    });
+
+    location.hash = "#test";
+</script>

+ 2 - 0
Tests/LibWeb/Text/expected/navigation/hashchange-event.txt

@@ -0,0 +1,2 @@
+  hashchange oldURL.hash= newURL.hash=#test
+hashchange oldURL.hash=#test newURL.hash=#done

+ 13 - 0
Tests/LibWeb/Text/input/navigation/hashchange-event.html

@@ -0,0 +1,13 @@
+<script src="../include.js"></script>
+<iframe src="../../data/iframe-hashchange-event.html"></iframe>
+<script>
+    asyncTest(done => {
+        let messageCount = 0;
+        window.addEventListener("message", event => {
+            println(event.data);
+            messageCount++;
+            if (messageCount == 2)
+                done();
+        });
+    });
+</script>

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

@@ -277,6 +277,7 @@ set(SOURCES
     HTML/FormControlInfrastructure.cpp
     HTML/FormDataEvent.cpp
     HTML/GlobalEventHandlers.cpp
+    HTML/HashChangeEvent.cpp
     HTML/History.cpp
     HTML/HTMLAllCollection.cpp
     HTML/HTMLAnchorElement.cpp

+ 14 - 4
Userland/Libraries/LibWeb/DOM/Document.cpp

@@ -79,6 +79,7 @@
 #include <LibWeb/HTML/HTMLObjectElement.h>
 #include <LibWeb/HTML/HTMLScriptElement.h>
 #include <LibWeb/HTML/HTMLTitleElement.h>
+#include <LibWeb/HTML/HashChangeEvent.h>
 #include <LibWeb/HTML/ListOfAvailableImages.h>
 #include <LibWeb/HTML/Location.h>
 #include <LibWeb/HTML/MessageEvent.h>
@@ -4023,10 +4024,19 @@ void Document::update_for_history_step_application(JS::NonnullGCPtr<HTML::Sessio
 
             // FIXME: 3. Restore persisted state given entry.
 
-            // FIXME: 4. If oldURL's fragment is not equal to entry's URL's fragment, then queue a global task on the DOM manipulation task source
-            //           given document's relevant global object to fire an event named hashchange at document's relevant global object,
-            //           using HashChangeEvent, with the oldURL attribute initialized to the serialization of oldURL and the newURL attribute
-            //           initialized to the serialization of entry's URL.
+            // 4. If oldURL's fragment is not equal to entry's URL's fragment, then queue a global task on the DOM manipulation task source
+            //    given document's relevant global object to fire an event named hashchange at document's relevant global object,
+            //    using HashChangeEvent, with the oldURL attribute initialized to the serialization of oldURL and the newURL attribute
+            //    initialized to the serialization of entry's URL.
+            if (old_url.fragment() != entry->url().fragment()) {
+                HTML::HashChangeEventInit hashchange_event_init;
+                hashchange_event_init.old_url = MUST(String::from_byte_string(old_url.serialize()));
+                hashchange_event_init.new_url = MUST(String::from_byte_string(entry->url().serialize()));
+                auto hashchange_event = HTML::HashChangeEvent::create(realm(), "hashchange"_fly_string, hashchange_event_init);
+                HTML::queue_global_task(HTML::Task::Source::DOMManipulation, relevant_global_object, [hashchange_event, &relevant_global_object]() {
+                    relevant_global_object.dispatch_event(hashchange_event);
+                });
+            }
         }
 
         // 6. Otherwise:

+ 45 - 0
Userland/Libraries/LibWeb/HTML/HashChangeEvent.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/HashChangeEventPrototype.h>
+#include <LibWeb/Bindings/Intrinsics.h>
+#include <LibWeb/HTML/HashChangeEvent.h>
+
+namespace Web::HTML {
+
+JS_DEFINE_ALLOCATOR(HashChangeEvent);
+
+[[nodiscard]] JS::NonnullGCPtr<HashChangeEvent> HashChangeEvent::create(JS::Realm& realm, FlyString const& event_name, HashChangeEventInit const& event_init)
+{
+    return realm.heap().allocate<HashChangeEvent>(realm, realm, event_name, event_init);
+}
+
+JS::NonnullGCPtr<HashChangeEvent> HashChangeEvent::construct_impl(JS::Realm& realm, FlyString const& event_name, HashChangeEventInit const& event_init)
+{
+    return realm.heap().allocate<HashChangeEvent>(realm, realm, event_name, event_init);
+}
+
+HashChangeEvent::HashChangeEvent(JS::Realm& realm, FlyString const& event_name, HashChangeEventInit const& event_init)
+    : DOM::Event(realm, event_name, event_init)
+    , m_old_url(event_init.old_url)
+    , m_new_url(event_init.new_url)
+{
+}
+
+void HashChangeEvent::initialize(JS::Realm& realm)
+{
+    Base::initialize(realm);
+    WEB_SET_PROTOTYPE_FOR_INTERFACE(HashChangeEvent);
+}
+
+void HashChangeEvent::visit_edges(JS::Cell::Visitor& visitor)
+{
+    Base::visit_edges(visitor);
+}
+
+}

+ 39 - 0
Userland/Libraries/LibWeb/HTML/HashChangeEvent.h

@@ -0,0 +1,39 @@
+/*
+ * 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 HashChangeEventInit : public DOM::EventInit {
+    String old_url;
+    String new_url;
+};
+
+class HashChangeEvent final : public DOM::Event {
+    WEB_PLATFORM_OBJECT(HashChangeEvent, DOM::Event);
+    JS_DECLARE_ALLOCATOR(HashChangeEvent);
+
+public:
+    [[nodiscard]] static JS::NonnullGCPtr<HashChangeEvent> create(JS::Realm&, FlyString const& event_name, HashChangeEventInit const&);
+    [[nodiscard]] static JS::NonnullGCPtr<HashChangeEvent> construct_impl(JS::Realm&, FlyString const& event_name, HashChangeEventInit const&);
+
+    String old_url() const { return m_old_url; }
+    String new_url() const { return m_new_url; }
+
+private:
+    HashChangeEvent(JS::Realm&, FlyString const& event_name, HashChangeEventInit const& event_init);
+
+    virtual void initialize(JS::Realm&) override;
+    virtual void visit_edges(JS::Cell::Visitor& visitor) override;
+
+    String m_old_url;
+    String m_new_url;
+};
+
+}

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

@@ -0,0 +1,15 @@
+#import <DOM/Event.idl>
+
+// https://html.spec.whatwg.org/multipage/nav-history-apis.html#hashchangeevent
+[Exposed=Window]
+interface HashChangeEvent : Event {
+  constructor(DOMString type, optional HashChangeEventInit eventInitDict = {});
+
+  readonly attribute USVString oldURL;
+  readonly attribute USVString newURL;
+};
+
+dictionary HashChangeEventInit : EventInit {
+  USVString oldURL = "";
+  USVString newURL = "";
+};

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

@@ -102,6 +102,7 @@ libweb_js_bindings(HTML/DOMStringMap)
 libweb_js_bindings(HTML/DataTransfer)
 libweb_js_bindings(HTML/ErrorEvent)
 libweb_js_bindings(HTML/FormDataEvent)
+libweb_js_bindings(HTML/HashChangeEvent)
 libweb_js_bindings(HTML/History)
 libweb_js_bindings(HTML/HTMLAllCollection)
 libweb_js_bindings(HTML/HTMLAnchorElement)