LibWeb: Implement inform the navigation api about aborting navigation

This also requires implementing the abort the ongoing navigation AO on
Navigation, which will be used from other NavigateEvent AOs.
This commit is contained in:
Andrew Kaster 2023-09-20 23:21:16 -06:00 committed by Andrew Kaster
parent 25ffe6becb
commit f296382e1a
5 changed files with 93 additions and 1 deletions

View file

@ -286,7 +286,8 @@ void Navigable::set_ongoing_navigation(Variant<Empty, Traversal, String> 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();
});
}
}

View file

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

View file

@ -62,6 +62,8 @@ public:
virtual ~NavigateEvent() override;
JS::NonnullGCPtr<DOM::AbortController> abort_controller() const { return *m_abort_controller; }
private:
NavigateEvent(JS::Realm&, FlyString const& event_name, NavigateEventInit const& event_init);

View file

@ -10,7 +10,9 @@
#include <LibWeb/Bindings/ExceptionOrUtils.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/Bindings/NavigationPrototype.h>
#include <LibWeb/DOM/AbortController.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/HTML/ErrorEvent.h>
#include <LibWeb/HTML/NavigateEvent.h>
#include <LibWeb/HTML/Navigation.h>
#include <LibWeb/HTML/NavigationCurrentEntryChangeEvent.h>
@ -702,4 +704,66 @@ WebIDL::ExceptionOr<NavigationResult> 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<JS::NonnullGCPtr<WebIDL::DOMException>> 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;
}
}
}

View file

@ -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<JS::NonnullGCPtr<WebIDL::DOMException>> error = {});
virtual ~Navigation() override;
// Internal Getters
JS::GCPtr<NavigateEvent> ongoing_navigate_event() const { return m_ongoing_navigate_event; }
private:
explicit Navigation(JS::Realm&);