Przeglądaj źródła

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.
Andrew Kaster 1 rok temu
rodzic
commit
f296382e1a

+ 21 - 1
Userland/Libraries/LibWeb/HTML/Navigable.cpp

@@ -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();
+    });
+}
+
 }

+ 2 - 0
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;
 

+ 2 - 0
Userland/Libraries/LibWeb/HTML/NavigateEvent.h

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

+ 64 - 0
Userland/Libraries/LibWeb/HTML/Navigation.cpp

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

+ 4 - 0
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<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&);