From 3c1d4eab2412b020fcfae4fde94fdc824b2f7d9c Mon Sep 17 00:00:00 2001 From: Andrew Kaster Date: Wed, 23 Aug 2023 10:47:05 -0600 Subject: [PATCH] LibWeb: Add NavigationHistoryEntry, a wrapper around SessionHistoryEntry --- .../Userland/Libraries/LibWeb/HTML/BUILD.gn | 1 + .../Userland/Libraries/LibWeb/idl_files.gni | 1 + Userland/Libraries/LibWeb/CMakeLists.txt | 1 + Userland/Libraries/LibWeb/Forward.h | 1 + Userland/Libraries/LibWeb/HTML/EventNames.h | 1 + .../LibWeb/HTML/NavigationHistoryEntry.cpp | 148 ++++++++++++++++++ .../LibWeb/HTML/NavigationHistoryEntry.h | 44 ++++++ .../LibWeb/HTML/NavigationHistoryEntry.idl | 16 ++ Userland/Libraries/LibWeb/idl_files.cmake | 1 + 9 files changed, 214 insertions(+) create mode 100644 Userland/Libraries/LibWeb/HTML/NavigationHistoryEntry.cpp create mode 100644 Userland/Libraries/LibWeb/HTML/NavigationHistoryEntry.h create mode 100644 Userland/Libraries/LibWeb/HTML/NavigationHistoryEntry.idl diff --git a/Meta/gn/secondary/Userland/Libraries/LibWeb/HTML/BUILD.gn b/Meta/gn/secondary/Userland/Libraries/LibWeb/HTML/BUILD.gn index c2c2a86832..3d90480ec8 100644 --- a/Meta/gn/secondary/Userland/Libraries/LibWeb/HTML/BUILD.gn +++ b/Meta/gn/secondary/Userland/Libraries/LibWeb/HTML/BUILD.gn @@ -123,6 +123,7 @@ source_set("HTML") { "MimeTypeArray.cpp", "Navigable.cpp", "NavigableContainer.cpp", + "NavigationHistoryEntry.cpp", "Navigator.cpp", "NavigatorID.cpp", "PageTransitionEvent.cpp", diff --git a/Meta/gn/secondary/Userland/Libraries/LibWeb/idl_files.gni b/Meta/gn/secondary/Userland/Libraries/LibWeb/idl_files.gni index 94ea926555..5a3c2aa7a8 100644 --- a/Meta/gn/secondary/Userland/Libraries/LibWeb/idl_files.gni +++ b/Meta/gn/secondary/Userland/Libraries/LibWeb/idl_files.gni @@ -182,6 +182,7 @@ standard_idl_files = [ "//Userland/Libraries/LibWeb/HTML/MessagePort.idl", "//Userland/Libraries/LibWeb/HTML/MimeType.idl", "//Userland/Libraries/LibWeb/HTML/MimeTypeArray.idl", + "//Userland/Libraries/LibWeb/HTML/NavigationHistoryEntry.idl", "//Userland/Libraries/LibWeb/HTML/Navigator.idl", "//Userland/Libraries/LibWeb/HTML/PageTransitionEvent.idl", "//Userland/Libraries/LibWeb/HTML/Path2D.idl", diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index 138e169f34..4e2af82f84 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -350,6 +350,7 @@ set(SOURCES HTML/MimeTypeArray.cpp HTML/Navigable.cpp HTML/NavigableContainer.cpp + HTML/NavigationHistoryEntry.cpp HTML/Navigator.cpp HTML/NavigatorID.cpp HTML/PageTransitionEvent.cpp diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h index 44332d2909..00b6adb829 100644 --- a/Userland/Libraries/LibWeb/Forward.h +++ b/Userland/Libraries/LibWeb/Forward.h @@ -414,6 +414,7 @@ class MimeType; class MimeTypeArray; class Navigable; class NavigableContainer; +class NavigationHistoryEntry; class Navigator; struct NavigationParams; class Origin; diff --git a/Userland/Libraries/LibWeb/HTML/EventNames.h b/Userland/Libraries/LibWeb/HTML/EventNames.h index b2b69f20f5..c5655e3a09 100644 --- a/Userland/Libraries/LibWeb/HTML/EventNames.h +++ b/Userland/Libraries/LibWeb/HTML/EventNames.h @@ -34,6 +34,7 @@ namespace Web::HTML::EventNames { __ENUMERATE_HTML_EVENT(contextmenu) \ __ENUMERATE_HTML_EVENT(copy) \ __ENUMERATE_HTML_EVENT(cuechange) \ + __ENUMERATE_HTML_EVENT(dispose) \ __ENUMERATE_HTML_EVENT(cut) \ __ENUMERATE_HTML_EVENT(DOMContentLoaded) \ __ENUMERATE_HTML_EVENT(drag) \ diff --git a/Userland/Libraries/LibWeb/HTML/NavigationHistoryEntry.cpp b/Userland/Libraries/LibWeb/HTML/NavigationHistoryEntry.cpp new file mode 100644 index 0000000000..4759d71363 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/NavigationHistoryEntry.cpp @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2023, Andrew Kaster + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Web::HTML { + +JS::NonnullGCPtr NavigationHistoryEntry::create(JS::Realm& realm, JS::NonnullGCPtr she) +{ + return realm.heap().allocate(realm, realm, she); +} + +NavigationHistoryEntry::NavigationHistoryEntry(JS::Realm& realm, JS::NonnullGCPtr she) + : DOM::EventTarget(realm) + , m_session_history_entry(she) +{ +} + +NavigationHistoryEntry::~NavigationHistoryEntry() = default; + +void NavigationHistoryEntry::initialize(JS::Realm& realm) +{ + Base::initialize(realm); + set_prototype(&Bindings::ensure_web_prototype(realm, "NavigationHistoryEntry")); +} + +void NavigationHistoryEntry::visit_edges(JS::Cell::Visitor& visitor) +{ + Base::visit_edges(visitor); + visitor.visit(m_session_history_entry); +} + +// https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-navigationhistoryentry-url +WebIDL::ExceptionOr> NavigationHistoryEntry::url() const +{ + // The url getter steps are: + // 1. Let document be this's relevant global object's associated Document. + auto& document = verify_cast(relevant_global_object(*this)).associated_document(); + + // 2. If document is not fully active, then return the empty string. + if (!document.is_fully_active()) + return String {}; + + // 3. Let she be this's session history entry. + auto const& she = this->m_session_history_entry; + + // 4. If she's document does not equal document, and she's document state's request referrer policy + // is "no-referrer" or "origin", then return null. + if ((she->document_state->document() != &document) + && (she->document_state->request_referrer_policy() == ReferrerPolicy::ReferrerPolicy::NoReferrer + || she->document_state->request_referrer_policy() == ReferrerPolicy::ReferrerPolicy::Origin)) + return OptionalNone {}; + + // 5. Return she's URL, serialized. + return TRY_OR_THROW_OOM(vm(), String::from_deprecated_string(she->url.serialize())); +} + +// https://html.spec.whatwg.org/multipage/nav-history-apis.html#concept-navigationhistoryentry-key +String NavigationHistoryEntry::key() const +{ + // The key of a NavigationHistoryEntry nhe is given by the return value of the following algorithm: + // 1. If nhe's relevant global object's associated Document is not fully active, then return the empty string. + auto& associated_document = verify_cast(relevant_global_object(*this)).associated_document(); + if (!associated_document.is_fully_active()) + return {}; + + // 2. Return nhe's session history entry's navigation API key. + return m_session_history_entry->navigation_api_key; +} + +// https://html.spec.whatwg.org/multipage/nav-history-apis.html#concept-navigationhistoryentry-id +String NavigationHistoryEntry::id() const +{ + // The ID of a NavigationHistoryEntry nhe is given by the return value of the following algorithm: + // 1. If nhe's relevant global object's associated Document is not fully active, then return the empty string. + auto& associated_document = verify_cast(relevant_global_object(*this)).associated_document(); + if (!associated_document.is_fully_active()) + return {}; + + // 2. Return nhe's session history entry's navigation API ID. + return m_session_history_entry->navigation_api_id; +} + +// https://html.spec.whatwg.org/multipage/nav-history-apis.html#concept-navigationhistoryentry-index +i64 NavigationHistoryEntry::index() const +{ + // The index of a NavigationHistoryEntry nhe is given by the return value of the following algorithm: + // 1. If nhe's relevant global object's associated Document is not fully active, then return −1. + auto& this_relevant_global_object = verify_cast(relevant_global_object(*this)); + if (!this_relevant_global_object.associated_document().is_fully_active()) + return -1; + + // FIXME 2. Return the result of getting the navigation API entry index of this's session history entry + // within this's relevant global object's navigation API. + return -1; +} + +// https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-navigationhistoryentry-samedocument +bool NavigationHistoryEntry::same_document() const +{ + // The sameDocument getter steps are: + // 1. Let document be this's relevant global object's associated Document. + auto& document = verify_cast(relevant_global_object(*this)).associated_document(); + + // 2. If document is not fully active, then return false. + if (!document.is_fully_active()) + return false; + + // 3. Return true if this's session history entry's document equals document, and false otherwise. + return m_session_history_entry->document_state->document() == &document; +} + +// https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-navigationhistoryentry-getstate +WebIDL::ExceptionOr NavigationHistoryEntry::get_state() +{ + // The getState() method steps are: + // 1. If this's relevant global object's associated Document is not fully active, then return undefined. + auto& associated_document = verify_cast(relevant_global_object(*this)).associated_document(); + if (!associated_document.is_fully_active()) + return JS::js_undefined(); + + // 2. Return StructuredDeserialize(this's session history entry's navigation API state). Rethrow any exceptions. + // NOTE: This can in theory throw an exception, if attempting to deserialize a large ArrayBuffer + // when not enough memory is available. + return structured_deserialize(vm(), m_session_history_entry->navigation_api_state, realm(), {}); +} + +void NavigationHistoryEntry::set_ondispose(WebIDL::CallbackType* event_handler) +{ + set_event_handler_attribute(HTML::EventNames::dispose, event_handler); +} + +WebIDL::CallbackType* NavigationHistoryEntry::ondispose() +{ + return event_handler_attribute(HTML::EventNames::dispose); +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/NavigationHistoryEntry.h b/Userland/Libraries/LibWeb/HTML/NavigationHistoryEntry.h new file mode 100644 index 0000000000..2297bb1881 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/NavigationHistoryEntry.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023, Andrew Kaster + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace Web::HTML { + +// https://html.spec.whatwg.org/multipage/nav-history-apis.html#navigationhistoryentry +class NavigationHistoryEntry : public DOM::EventTarget { + WEB_PLATFORM_OBJECT(NavigationHistoryEntry, DOM::EventTarget); + +public: + [[nodiscard]] static JS::NonnullGCPtr create(JS::Realm&, JS::NonnullGCPtr); + + WebIDL::ExceptionOr> url() const; + String key() const; + String id() const; + i64 index() const; + bool same_document() const; + WebIDL::ExceptionOr get_state(); + + void set_ondispose(WebIDL::CallbackType*); + WebIDL::CallbackType* ondispose(); + + // Non-spec'd getter, not exposed to JS + SessionHistoryEntry const& session_history_entry() const { return *m_session_history_entry; } + SessionHistoryEntry& session_history_entry() { return *m_session_history_entry; } + + virtual ~NavigationHistoryEntry() override; + +private: + NavigationHistoryEntry(JS::Realm&, JS::NonnullGCPtr); + + virtual void initialize(JS::Realm&) override; + virtual void visit_edges(JS::Cell::Visitor&) override; + + JS::NonnullGCPtr m_session_history_entry; +}; +} diff --git a/Userland/Libraries/LibWeb/HTML/NavigationHistoryEntry.idl b/Userland/Libraries/LibWeb/HTML/NavigationHistoryEntry.idl new file mode 100644 index 0000000000..5abfb02c67 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/NavigationHistoryEntry.idl @@ -0,0 +1,16 @@ +#import +#import + +// https://html.spec.whatwg.org/multipage/nav-history-apis.html#navigationtype +[Exposed=Window, UseNewAKString] +interface NavigationHistoryEntry : EventTarget { + readonly attribute USVString? url; + readonly attribute DOMString key; + readonly attribute DOMString id; + readonly attribute long long index; + readonly attribute boolean sameDocument; + + any getState(); + + attribute EventHandler ondispose; +}; diff --git a/Userland/Libraries/LibWeb/idl_files.cmake b/Userland/Libraries/LibWeb/idl_files.cmake index 67ef6ac296..a0ac9f27d2 100644 --- a/Userland/Libraries/LibWeb/idl_files.cmake +++ b/Userland/Libraries/LibWeb/idl_files.cmake @@ -168,6 +168,7 @@ libweb_js_bindings(HTML/MessageEvent) libweb_js_bindings(HTML/MessagePort) libweb_js_bindings(HTML/MimeType) libweb_js_bindings(HTML/MimeTypeArray) +libweb_js_bindings(HTML/NavigationHistoryEntry) libweb_js_bindings(HTML/Navigator) libweb_js_bindings(HTML/PageTransitionEvent) libweb_js_bindings(HTML/Path2D)