diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index dca6a1c41fd..da046c9cc45 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -411,6 +411,7 @@ set(SOURCES HTML/NavigationDestination.cpp HTML/NavigationCurrentEntryChangeEvent.cpp HTML/NavigationHistoryEntry.cpp + HTML/NavigationObserver.cpp HTML/NavigationParams.cpp HTML/NavigationTransition.cpp HTML/Navigator.cpp diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h index 6392ad692c3..aba1ae04b07 100644 --- a/Userland/Libraries/LibWeb/Forward.h +++ b/Userland/Libraries/LibWeb/Forward.h @@ -496,6 +496,7 @@ class Navigation; class NavigationCurrentEntryChangeEvent; class NavigationDestination; class NavigationHistoryEntry; +class NavigationObserver; class NavigationTransition; class Navigator; class PageTransitionEvent; diff --git a/Userland/Libraries/LibWeb/HTML/Navigable.cpp b/Userland/Libraries/LibWeb/HTML/Navigable.cpp index 9dcf7b4aa8f..56e3bd3b090 100644 --- a/Userland/Libraries/LibWeb/HTML/Navigable.cpp +++ b/Userland/Libraries/LibWeb/HTML/Navigable.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -126,6 +127,7 @@ void Navigable::visit_edges(Cell::Visitor& visitor) visitor.visit(m_current_session_history_entry); visitor.visit(m_active_session_history_entry); visitor.visit(m_container); + visitor.visit(m_navigation_observers); m_event_handler.visit_edges(visitor); } @@ -230,6 +232,13 @@ void Navigable::activate_history_entry(JS::GCPtr entry) // 5. Make active newDocument. new_document->make_active(); + + if (m_ongoing_navigation.has()) { + for (auto navigation_observer : m_navigation_observers) { + if (navigation_observer->navigation_complete()) + navigation_observer->navigation_complete()->function()(); + } + } } // https://html.spec.whatwg.org/multipage/document-sequences.html#nav-document @@ -2200,4 +2209,16 @@ void Navigable::paste(String const& text) m_event_handler.handle_paste(text); } +void Navigable::register_navigation_observer(Badge, NavigationObserver& navigation_observer) +{ + auto result = m_navigation_observers.set(navigation_observer); + VERIFY(result == AK::HashSetResult::InsertedNewEntry); +} + +void Navigable::unregister_navigation_observer(Badge, NavigationObserver& navigation_observer) +{ + bool was_removed = m_navigation_observers.remove(navigation_observer); + VERIFY(was_removed); +} + } diff --git a/Userland/Libraries/LibWeb/HTML/Navigable.h b/Userland/Libraries/LibWeb/HTML/Navigable.h index 8283dff578d..5d65b99ea9e 100644 --- a/Userland/Libraries/LibWeb/HTML/Navigable.h +++ b/Userland/Libraries/LibWeb/HTML/Navigable.h @@ -59,6 +59,9 @@ public: ErrorOr initialize_navigable(JS::NonnullGCPtr document_state, JS::GCPtr parent); + void register_navigation_observer(Badge, NavigationObserver&); + void unregister_navigation_observer(Badge, NavigationObserver&); + Vector> child_navigables() const; bool is_traversable() const; @@ -232,6 +235,8 @@ private: JS::NonnullGCPtr m_page; + HashTable> m_navigation_observers; + bool m_has_been_destroyed { false }; CSSPixelSize m_size; diff --git a/Userland/Libraries/LibWeb/HTML/NavigationObserver.cpp b/Userland/Libraries/LibWeb/HTML/NavigationObserver.cpp new file mode 100644 index 00000000000..7b95ccfa0e7 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/NavigationObserver.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2024, Tim Flynn + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +namespace Web::HTML { + +JS_DEFINE_ALLOCATOR(NavigationObserver); + +NavigationObserver::NavigationObserver(JS::Realm& realm, Navigable& navigable) + : Bindings::PlatformObject(realm) + , m_navigable(navigable) +{ + m_navigable->register_navigation_observer({}, *this); +} + +void NavigationObserver::visit_edges(Cell::Visitor& visitor) +{ + Base::visit_edges(visitor); + visitor.visit(m_navigable); + visitor.visit(m_navigation_complete); +} + +void NavigationObserver::finalize() +{ + Base::finalize(); + m_navigable->unregister_navigation_observer({}, *this); +} + +void NavigationObserver::set_navigation_complete(Function callback) +{ + if (callback) + m_navigation_complete = JS::create_heap_function(vm().heap(), move(callback)); + else + m_navigation_complete = nullptr; +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/NavigationObserver.h b/Userland/Libraries/LibWeb/HTML/NavigationObserver.h new file mode 100644 index 00000000000..03be96d23b9 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/NavigationObserver.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024, Tim Flynn + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace Web::HTML { + +class NavigationObserver final : public Bindings::PlatformObject { + WEB_PLATFORM_OBJECT(NavigationObserver, Bindings::PlatformObject); + JS_DECLARE_ALLOCATOR(NavigationObserver); + +public: + [[nodiscard]] JS::GCPtr> navigation_complete() const { return m_navigation_complete; } + void set_navigation_complete(Function); + +private: + NavigationObserver(JS::Realm&, Navigable&); + + virtual void visit_edges(Cell::Visitor&) override; + virtual void finalize() override; + + JS::NonnullGCPtr m_navigable; + JS::GCPtr> m_navigation_complete; +}; + +}