diff --git a/Libraries/LibWeb/HTML/Navigable.cpp b/Libraries/LibWeb/HTML/Navigable.cpp index d0949bfcc5c..fa42639b6b1 100644 --- a/Libraries/LibWeb/HTML/Navigable.cpp +++ b/Libraries/LibWeb/HTML/Navigable.cpp @@ -1327,7 +1327,8 @@ WebIDL::ExceptionOr Navigable::populate_session_history_entry_document( // an optional serialized state-or-null navigationAPIState (default null), // an optional entry list or null formDataEntryList (default null), // an optional referrer policy referrerPolicy (default the empty string), -// and an optional user navigation involvement userInvolvement (default "none"): +// an optional user navigation involvement userInvolvement (default "none"), +// and an optional Element sourceElement (default null): // https://html.spec.whatwg.org/multipage/browsing-the-web.html#navigate WebIDL::ExceptionOr Navigable::navigate(NavigateParams params) @@ -1346,6 +1347,7 @@ WebIDL::ExceptionOr Navigable::navigate(NavigateParams params) auto const& form_data_entry_list = params.form_data_entry_list; auto referrer_policy = params.referrer_policy; auto user_involvement = params.user_involvement; + auto source_element = params.source_element; auto& active_document = *this->active_document(); auto& realm = active_document.realm(); auto& vm = this->vm(); @@ -1429,8 +1431,8 @@ WebIDL::ExceptionOr Navigable::navigate(NavigateParams params) && !response && url.equals(active_session_history_entry()->url(), URL::ExcludeFragment::Yes) && url.fragment().has_value()) { - // 1. Navigate to a fragment given navigable, url, historyHandling, userInvolvement, navigationAPIState, and navigationId. - TRY(navigate_to_a_fragment(url, to_history_handling_behavior(history_handling), user_involvement, navigation_api_state, navigation_id)); + // 1. Navigate to a fragment given navigable, url, historyHandling, userInvolvement, sourceElement, navigationAPIState, and navigationId. + TRY(navigate_to_a_fragment(url, to_history_handling_behavior(history_handling), user_involvement, source_element, navigation_api_state, navigation_id)); // 2. Return. return {}; @@ -1492,7 +1494,8 @@ WebIDL::ExceptionOr Navigable::navigate(NavigateParams params) // 4. Let continue be the result of firing a push/replace/reload navigate event at navigation // with navigationType set to historyHandling, isSameDocument set to false, userInvolvement set to userInvolvement, - // formDataEntryList set to entryListForFiring, destinationURL set to url, and navigationAPIState set to navigationAPIStateForFiring. + // sourceElement set to sourceElement, formDataEntryList set to entryListForFiring, destinationURL set to url, + // and navigationAPIState set to navigationAPIStateForFiring. auto navigation_type = [](Bindings::NavigationHistoryBehavior history_handling) { switch (history_handling) { case Bindings::NavigationHistoryBehavior::Push: @@ -1504,7 +1507,7 @@ WebIDL::ExceptionOr Navigable::navigate(NavigateParams params) VERIFY_NOT_REACHED(); } }(history_handling); - auto continue_ = navigation->fire_a_push_replace_reload_navigate_event(navigation_type, url, false, user_involvement, entry_list_for_firing, navigation_api_state_for_firing); + auto continue_ = navigation->fire_a_push_replace_reload_navigate_event(navigation_type, url, false, user_involvement, source_element, entry_list_for_firing, navigation_api_state_for_firing); // 5. If continue is false, then return. if (!continue_) @@ -1611,7 +1614,7 @@ WebIDL::ExceptionOr Navigable::navigate(NavigateParams params) } // https://html.spec.whatwg.org/multipage/browsing-the-web.html#navigate-fragid -WebIDL::ExceptionOr Navigable::navigate_to_a_fragment(URL::URL const& url, HistoryHandlingBehavior history_handling, UserNavigationInvolvement user_involvement, Optional navigation_api_state, String navigation_id) +WebIDL::ExceptionOr Navigable::navigate_to_a_fragment(URL::URL const& url, HistoryHandlingBehavior history_handling, UserNavigationInvolvement user_involvement, GC::Ptr source_element, Optional navigation_api_state, String navigation_id) { (void)navigation_id; @@ -1623,10 +1626,11 @@ WebIDL::ExceptionOr Navigable::navigate_to_a_fragment(URL::URL const& url, // 3. If navigationAPIState is not null, then set destinationNavigationAPIState to navigationAPIState. auto destination_navigation_api_state = navigation_api_state.has_value() ? *navigation_api_state : active_session_history_entry()->navigation_api_state(); - // 4. Let continue be the result of firing a push/replace/reload navigate event at navigation with navigationType set to historyHandling, isSameDocument set to true, - // userInvolvement set to userInvolvement, and destinationURL set to url, and navigationAPIState set to destinationNavigationAPIState. + // 4. Let continue be the result of firing a push/replace/reload navigate event at navigation with navigationType + // set to historyHandling, isSameDocument set to true, userInvolvement set to userInvolvement, sourceElement set + // to sourceElement, destinationURL set to url, and navigationAPIState set to destinationNavigationAPIState. auto navigation_type = history_handling == HistoryHandlingBehavior::Push ? Bindings::NavigationType::Push : Bindings::NavigationType::Replace; - bool const continue_ = navigation->fire_a_push_replace_reload_navigate_event(navigation_type, url, true, user_involvement, {}, destination_navigation_api_state); + bool const continue_ = navigation->fire_a_push_replace_reload_navigate_event(navigation_type, url, true, user_involvement, source_element, {}, destination_navigation_api_state); // 5. If continue is false, then return. if (!continue_) diff --git a/Libraries/LibWeb/HTML/Navigable.h b/Libraries/LibWeb/HTML/Navigable.h index 6b3052ac7ee..3fef494ce22 100644 --- a/Libraries/LibWeb/HTML/Navigable.h +++ b/Libraries/LibWeb/HTML/Navigable.h @@ -146,11 +146,12 @@ public: Optional&> form_data_entry_list = {}; ReferrerPolicy::ReferrerPolicy referrer_policy = ReferrerPolicy::ReferrerPolicy::EmptyString; UserNavigationInvolvement user_involvement = UserNavigationInvolvement::None; + GC::Ptr source_element = nullptr; }; WebIDL::ExceptionOr navigate(NavigateParams); - WebIDL::ExceptionOr navigate_to_a_fragment(URL::URL const&, HistoryHandlingBehavior, UserNavigationInvolvement, Optional navigation_api_state, String navigation_id); + WebIDL::ExceptionOr navigate_to_a_fragment(URL::URL const&, HistoryHandlingBehavior, UserNavigationInvolvement, GC::Ptr source_element, Optional navigation_api_state, String navigation_id); GC::Ptr evaluate_javascript_url(URL::URL const&, URL::Origin const& new_document_origin, UserNavigationInvolvement, String navigation_id); void navigate_to_a_javascript_url(URL::URL const&, HistoryHandlingBehavior, URL::Origin const& initiator_origin, UserNavigationInvolvement, CSPNavigationType csp_navigation_type, String navigation_id); diff --git a/Libraries/LibWeb/HTML/NavigateEvent.cpp b/Libraries/LibWeb/HTML/NavigateEvent.cpp index 05f9ee1b524..e14a05b001e 100644 --- a/Libraries/LibWeb/HTML/NavigateEvent.cpp +++ b/Libraries/LibWeb/HTML/NavigateEvent.cpp @@ -42,6 +42,7 @@ NavigateEvent::NavigateEvent(JS::Realm& realm, FlyString const& event_name, Navi , m_download_request(event_init.download_request) , m_info(event_init.info.value_or(JS::js_undefined())) , m_has_ua_visual_transition(event_init.has_ua_visual_transition) + , m_source_element(event_init.source_element) { } @@ -62,6 +63,7 @@ void NavigateEvent::visit_edges(JS::Cell::Visitor& visitor) visitor.visit(m_signal); visitor.visit(m_form_data); visitor.visit(m_info); + visitor.visit(m_source_element); } // https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-navigateevent-intercept diff --git a/Libraries/LibWeb/HTML/NavigateEvent.h b/Libraries/LibWeb/HTML/NavigateEvent.h index 3b23533bbdb..2894c58a1c0 100644 --- a/Libraries/LibWeb/HTML/NavigateEvent.h +++ b/Libraries/LibWeb/HTML/NavigateEvent.h @@ -9,7 +9,6 @@ #include #include #include -#include namespace Web::HTML { @@ -25,6 +24,7 @@ struct NavigateEventInit : public DOM::EventInit { Optional download_request = {}; Optional info; bool has_ua_visual_transition = false; + GC::Ptr source_element = nullptr; }; // https://html.spec.whatwg.org/multipage/nav-history-apis.html#navigationintercepthandler @@ -54,8 +54,8 @@ public: [[nodiscard]] static GC::Ref construct_impl(JS::Realm&, FlyString const& event_name, NavigateEventInit const&); - // The navigationType, destination, canIntercept, userInitiated, hashChange, signal, formData, - // downloadRequest, info, and hasUAVisualTransition attributes must return the values they are initialized to. + // The navigationType, destination, canIntercept, userInitiated, hashChange, signal, formData, downloadRequest, + // info, hasUAVisualTransition, and sourceElement attributes must return the values they are initialized to. Bindings::NavigationType navigation_type() const { return m_navigation_type; } GC::Ref destination() const { return m_destination; } bool can_intercept() const { return m_can_intercept; } @@ -66,6 +66,7 @@ public: Optional download_request() const { return m_download_request; } JS::Value info() const { return m_info; } bool has_ua_visual_transition() const { return m_has_ua_visual_transition; } + GC::Ptr source_element() const { return m_source_element; } WebIDL::ExceptionOr intercept(NavigationInterceptOptions const&); WebIDL::ExceptionOr scroll(); @@ -141,6 +142,9 @@ private: // https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-navigateevent-hasuavisualtransition bool m_has_ua_visual_transition { false }; + + // https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-navigateevent-sourceelement + GC::Ptr m_source_element { nullptr }; }; } diff --git a/Libraries/LibWeb/HTML/NavigateEvent.idl b/Libraries/LibWeb/HTML/NavigateEvent.idl index 600d18fbe81..b1f96425fd3 100644 --- a/Libraries/LibWeb/HTML/NavigateEvent.idl +++ b/Libraries/LibWeb/HTML/NavigateEvent.idl @@ -19,6 +19,7 @@ interface NavigateEvent : Event { readonly attribute DOMString? downloadRequest; readonly attribute any info; readonly attribute boolean hasUAVisualTransition; + readonly attribute Element? sourceElement; undefined intercept(optional NavigationInterceptOptions options = {}); undefined scroll(); @@ -35,6 +36,7 @@ dictionary NavigateEventInit : EventInit { DOMString? downloadRequest = null; any info; boolean hasUAVisualTransition = false; + Element? sourceElement = null; }; dictionary NavigationInterceptOptions { diff --git a/Libraries/LibWeb/HTML/Navigation.cpp b/Libraries/LibWeb/HTML/Navigation.cpp index 0d171f121d8..a391f1c6967 100644 --- a/Libraries/LibWeb/HTML/Navigation.cpp +++ b/Libraries/LibWeb/HTML/Navigation.cpp @@ -911,6 +911,7 @@ bool Navigation::inner_navigate_event_firing_algorithm( Bindings::NavigationType navigation_type, GC::Ref destination, UserNavigationInvolvement user_involvement, + GC::Ptr source_element, Optional&> form_data_entry_list, Optional download_request_filename, Optional classic_history_api_state) @@ -1011,6 +1012,9 @@ bool Navigation::inner_navigate_event_firing_algorithm( // of the document's latest entry, was done by the user agent. Otherwise, initialize it to false. event_init.has_ua_visual_transition = false; + // 18. Initialize event's sourceElement to sourceElement. + event_init.source_element = source_element; + // 18. Set event's abort controller to a new AbortController created in navigation's relevant realm. // AD-HOC: Set on the NavigateEvent later after construction auto abort_controller = MUST(DOM::AbortController::construct_impl(realm)); @@ -1291,9 +1295,10 @@ bool Navigation::fire_a_traverse_navigate_event(GC::Ref des // navigation's relevant global object's associated Document; otherwise false. destination->set_is_same_document(destination_she->document() == &as(relevant_global_object(*this)).associated_document()); - // 9. Return the result of performing the inner navigate event firing algorithm given navigation, "traverse", event, destination, userInvolvement, null, and null. + // 9. Return the result of performing the inner navigate event firing algorithm given navigation, "traverse", + // event, destination, userInvolvement, sourceElement, null, and null. // AD-HOC: We don't pass the event, but we do pass the classic_history_api state at the end to be set later - return inner_navigate_event_firing_algorithm(Bindings::NavigationType::Traverse, destination, user_involvement, {}, {}, {}); + return inner_navigate_event_firing_algorithm(Bindings::NavigationType::Traverse, destination, user_involvement, {}, {}, {}, {}); } // https://html.spec.whatwg.org/multipage/nav-history-apis.html#fire-a-push/replace/reload-navigate-event @@ -1302,6 +1307,7 @@ bool Navigation::fire_a_push_replace_reload_navigate_event( URL::URL destination_url, bool is_same_document, UserNavigationInvolvement user_involvement, + GC::Ptr source_element, Optional&> form_data_entry_list, Optional navigation_api_state, Optional classic_history_api_state) @@ -1333,13 +1339,13 @@ bool Navigation::fire_a_push_replace_reload_navigate_event( destination->set_is_same_document(is_same_document); // 8. Return the result of performing the inner navigate event firing algorithm given navigation, - // navigationType, event, destination, userInvolvement, formDataEntryList, and null. + // navigationType, event, destination, userInvolvement, sourceElement, formDataEntryList, and null. // AD-HOC: We don't pass the event, but we do pass the classic_history_api state at the end to be set later - return inner_navigate_event_firing_algorithm(navigation_type, destination, user_involvement, move(form_data_entry_list), {}, move(classic_history_api_state)); + return inner_navigate_event_firing_algorithm(navigation_type, destination, user_involvement, source_element, move(form_data_entry_list), {}, move(classic_history_api_state)); } // https://html.spec.whatwg.org/multipage/nav-history-apis.html#fire-a-download-request-navigate-event -bool Navigation::fire_a_download_request_navigate_event(URL::URL destination_url, UserNavigationInvolvement user_involvement, String filename) +bool Navigation::fire_a_download_request_navigate_event(URL::URL destination_url, UserNavigationInvolvement user_involvement, GC::Ptr source_element, String filename) { auto& realm = relevant_realm(*this); auto& vm = this->vm(); @@ -1364,9 +1370,9 @@ bool Navigation::fire_a_download_request_navigate_event(URL::URL destination_url destination->set_is_same_document(false); // 8. Return the result of performing the inner navigate event firing algorithm given navigation, - // "push", event, destination, userInvolvement, null, and filename. + // "push", event, destination, userInvolvement, sourceElement, null, and filename. // AD-HOC: We don't pass the event, but we do pass the classic_history_api state at the end to be set later - return inner_navigate_event_firing_algorithm(Bindings::NavigationType::Push, destination, user_involvement, {}, move(filename), {}); + return inner_navigate_event_firing_algorithm(Bindings::NavigationType::Push, destination, user_involvement, source_element, {}, move(filename), {}); } // https://html.spec.whatwg.org/multipage/nav-history-apis.html#initialize-the-navigation-api-entries-for-a-new-document diff --git a/Libraries/LibWeb/HTML/Navigation.h b/Libraries/LibWeb/HTML/Navigation.h index 342b962b7c8..767679714ac 100644 --- a/Libraries/LibWeb/HTML/Navigation.h +++ b/Libraries/LibWeb/HTML/Navigation.h @@ -116,10 +116,11 @@ public: URL::URL destination_url, bool is_same_document, UserNavigationInvolvement = UserNavigationInvolvement::None, + GC::Ptr source_element = {}, Optional&> form_data_entry_list = {}, Optional navigation_api_state = {}, Optional classic_history_api_state = {}); - bool fire_a_download_request_navigate_event(URL::URL destination_url, UserNavigationInvolvement user_involvement, String filename); + bool fire_a_download_request_navigate_event(URL::URL destination_url, UserNavigationInvolvement user_involvement, GC::Ptr source_element, String filename); void initialize_the_navigation_api_entries_for_a_new_document(Vector> const& new_shes, GC::Ref initial_she); void update_the_navigation_api_entries_for_a_same_document_navigation(GC::Ref destination_she, Bindings::NavigationType); @@ -154,6 +155,7 @@ private: Bindings::NavigationType, GC::Ref, UserNavigationInvolvement, + GC::Ptr source_element, Optional&> form_data_entry_list, Optional download_request_filename, Optional classic_history_api_state); diff --git a/Tests/LibWeb/Text/input/wpt-import/navigation-api/navigate-event/event-constructor.html b/Tests/LibWeb/Text/input/wpt-import/navigation-api/navigate-event/event-constructor.html index a7d86b9a75f..d83c3cdf283 100644 --- a/Tests/LibWeb/Text/input/wpt-import/navigation-api/navigate-event/event-constructor.html +++ b/Tests/LibWeb/Text/input/wpt-import/navigation-api/navigate-event/event-constructor.html @@ -78,8 +78,7 @@ async_test(t => { assert_equals(event.downloadRequest, downloadRequest); assert_equals(event.info, info); assert_equals(event.hasUAVisualTransition, hasUAVisualTransition); - // NavigateEvent sourceElement is still in development, so test whether it is available. - if ("sourceElement" in e) assert_equals(event.sourceElement, sourceElement); + assert_equals(event.sourceElement, sourceElement); }); history.pushState(2, null, "#2"); }, "all properties are reflected back"); @@ -99,8 +98,7 @@ async_test(t => { assert_equals(event.formData, null); assert_equals(event.downloadRequest, null); assert_equals(event.info, undefined); - // NavigateEvent sourceElement is still in development, so test whether it is available. - if ("sourceElement" in e) assert_equals(event.sourceElement, null); + assert_equals(event.sourceElement, null); }); history.pushState(3, null, "#3"); }, "defaults are as expected");