diff --git a/Userland/Libraries/LibWeb/HTML/Navigable.cpp b/Userland/Libraries/LibWeb/HTML/Navigable.cpp
index d4079c2262..3593084464 100644
--- a/Userland/Libraries/LibWeb/HTML/Navigable.cpp
+++ b/Userland/Libraries/LibWeb/HTML/Navigable.cpp
@@ -286,7 +286,8 @@ void Navigable::set_ongoing_navigation(Variant ongoing
if (m_ongoing_navigation == ongoing_navigation)
return;
- // FIXME: 2. Inform the navigation API about aborting navigation given navigable.
+ // 2. Inform the navigation API about aborting navigation given navigable.
+ inform_the_navigation_api_about_aborting_navigation();
// 3. Set navigable's ongoing navigation to newValue.
m_ongoing_navigation = ongoing_navigation;
@@ -1671,4 +1672,23 @@ bool Navigable::has_a_rendering_opportunity() const
return true;
}
+// https://html.spec.whatwg.org/multipage/nav-history-apis.html#inform-the-navigation-api-about-aborting-navigation
+void Navigable::inform_the_navigation_api_about_aborting_navigation()
+{
+ // FIXME: 1. If this algorithm is running on navigable's active window's relevant agent's event loop, then continue on to the following steps.
+ // Otherwise, queue a global task on the navigation and traversal task source given navigable's active window to run the following steps.
+
+ queue_global_task(Task::Source::NavigationAndTraversal, *active_window(), [this] {
+ // 2. Let navigation be navigable's active window's navigation API.
+ auto navigation = active_window()->navigation();
+
+ // 3. If navigation's ongoing navigate event is null, then return.
+ if (navigation->ongoing_navigate_event() == nullptr)
+ return;
+
+ // 4. Abort the ongoing navigation given navigation.
+ navigation->abort_the_ongoing_navigation();
+ });
+}
+
}
diff --git a/Userland/Libraries/LibWeb/HTML/Navigable.h b/Userland/Libraries/LibWeb/HTML/Navigable.h
index 69f4b287a4..b89fc3db48 100644
--- a/Userland/Libraries/LibWeb/HTML/Navigable.h
+++ b/Userland/Libraries/LibWeb/HTML/Navigable.h
@@ -171,6 +171,8 @@ private:
void scroll_offset_did_change();
+ void inform_the_navigation_api_about_aborting_navigation();
+
// https://html.spec.whatwg.org/multipage/document-sequences.html#nav-id
String m_id;
diff --git a/Userland/Libraries/LibWeb/HTML/NavigateEvent.h b/Userland/Libraries/LibWeb/HTML/NavigateEvent.h
index 4fea44ae51..211fdf0c22 100644
--- a/Userland/Libraries/LibWeb/HTML/NavigateEvent.h
+++ b/Userland/Libraries/LibWeb/HTML/NavigateEvent.h
@@ -62,6 +62,8 @@ public:
virtual ~NavigateEvent() override;
+ JS::NonnullGCPtr abort_controller() const { return *m_abort_controller; }
+
private:
NavigateEvent(JS::Realm&, FlyString const& event_name, NavigateEventInit const& event_init);
diff --git a/Userland/Libraries/LibWeb/HTML/Navigation.cpp b/Userland/Libraries/LibWeb/HTML/Navigation.cpp
index f88db5185e..b54d1af5fa 100644
--- a/Userland/Libraries/LibWeb/HTML/Navigation.cpp
+++ b/Userland/Libraries/LibWeb/HTML/Navigation.cpp
@@ -10,7 +10,9 @@
#include
#include
#include
+#include
#include
+#include
#include
#include
#include
@@ -702,4 +704,66 @@ WebIDL::ExceptionOr Navigation::perform_a_navigation_api_trave
return navigation_api_method_tracker_derived_result(api_method_tracker);
}
+// https://html.spec.whatwg.org/multipage/nav-history-apis.html#abort-the-ongoing-navigation
+void Navigation::abort_the_ongoing_navigation(Optional> error)
+{
+ auto& realm = relevant_realm(*this);
+
+ // To abort the ongoing navigation given a Navigation navigation and an optional DOMException error:
+
+ // 1. Let event be navigation's ongoing navigate event.
+ auto event = ongoing_navigate_event();
+
+ // 2. Assert: event is not null.
+ VERIFY(event != nullptr);
+
+ // 3. Set navigation's focus changed during ongoing navigation to false.
+ m_focus_changed_during_ongoing_navigation = false;
+
+ // 4. Set navigation's suppress normal scroll restoration during ongoing navigation to false.
+ m_suppress_scroll_restoration_during_ongoing_navigation = false;
+
+ // 5. If error was not given, then let error be a new "AbortError" DOMException created in navigation's relevant realm.
+ if (!error.has_value())
+ error = WebIDL::AbortError::create(realm, "Navigation aborted"_fly_string);
+
+ VERIFY(error.has_value());
+
+ // 6. If event's dispatch flag is set, then set event's canceled flag to true.
+ if (event->dispatched())
+ event->set_cancelled(true);
+
+ // 7. Signal abort on event's abort controller given error.
+ event->abort_controller()->abort(error.value());
+
+ // 8. Set navigation's ongoing navigate event to null.
+ m_ongoing_navigate_event = nullptr;
+
+ // 9. Fire an event named navigateerror at navigation using ErrorEvent, with error initialized to error,
+ // and message, filename, lineno, and colno initialized to appropriate values that can be extracted
+ // from error and the current JavaScript stack in the same underspecified way that the report the exception algorithm does.
+ ErrorEventInit event_init = {};
+ event_init.error = error.value();
+ // FIXME: Extract information from the exception and the JS context in the wishy-washy way the spec says here.
+ event_init.filename = String {};
+ event_init.colno = 0;
+ event_init.lineno = 0;
+ event_init.message = String {};
+
+ dispatch_event(ErrorEvent::create(realm, EventNames::navigateerror, event_init));
+
+ // 10. If navigation's ongoing API method tracker is non-null, then reject the finished promise for apiMethodTracker with error.
+ if (m_ongoing_api_method_tracker != nullptr)
+ WebIDL::reject_promise(realm, m_ongoing_api_method_tracker->finished_promise, error.value());
+
+ // 11. If navigation's transition is not null, then:
+ if (m_transition != nullptr) {
+ // 1. Reject navigation's transition's finished promise with error.
+ m_transition->finished()->reject(error.value());
+
+ // 2. Set navigation's transition to null.
+ m_transition = nullptr;
+ }
+}
+
}
diff --git a/Userland/Libraries/LibWeb/HTML/Navigation.h b/Userland/Libraries/LibWeb/HTML/Navigation.h
index eef25af99f..05d34b0f7f 100644
--- a/Userland/Libraries/LibWeb/HTML/Navigation.h
+++ b/Userland/Libraries/LibWeb/HTML/Navigation.h
@@ -106,9 +106,13 @@ public:
// Abstract Operations
bool has_entries_and_events_disabled() const;
i64 get_the_navigation_api_entry_index(SessionHistoryEntry const&) const;
+ void abort_the_ongoing_navigation(Optional> error = {});
virtual ~Navigation() override;
+ // Internal Getters
+ JS::GCPtr ongoing_navigate_event() const { return m_ongoing_navigate_event; }
+
private:
explicit Navigation(JS::Realm&);