mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 17:52:45 +00:00 
			
		
		
		
	LibWeb: Add assorted AOs related to populating the navigation API's SHEs
This commit is contained in:
		
							parent
							
								
									49e0466a3d
								
							
						
					
					
						commit
						a8091c009b
					
				
					 6 changed files with 246 additions and 1 deletions
				
			
		|  | @ -3605,6 +3605,21 @@ Painting::ViewportPaintable* Document::paintable() | |||
|     return static_cast<Painting::ViewportPaintable*>(Node::paintable()); | ||||
| } | ||||
| 
 | ||||
| // https://html.spec.whatwg.org/multipage/browsing-the-web.html#restore-the-history-object-state
 | ||||
| void Document::restore_the_history_object_state(JS::NonnullGCPtr<HTML::SessionHistoryEntry> entry) | ||||
| { | ||||
|     // 1. Let targetRealm be document's relevant realm.
 | ||||
|     auto& target_realm = HTML::relevant_realm(*this); | ||||
| 
 | ||||
|     // 2. Let state be StructuredDeserialize(entry's classic history API state, targetRealm). If this throws an exception, catch it and let state be null.
 | ||||
|     // 3. Set document's history object's state to state.
 | ||||
|     auto state_or_error = HTML::structured_deserialize(target_realm.vm(), entry->classic_history_api_state, target_realm, {}); | ||||
|     if (state_or_error.is_error()) | ||||
|         m_history->set_state(JS::js_null()); | ||||
|     else | ||||
|         m_history->set_state(state_or_error.release_value()); | ||||
| } | ||||
| 
 | ||||
| // https://html.spec.whatwg.org/multipage/browsing-the-web.html#update-document-for-history-step-application
 | ||||
| void Document::update_for_history_step_application(JS::NonnullGCPtr<HTML::SessionHistoryEntry> entry, bool do_not_reactive, size_t script_history_length, size_t script_history_index) | ||||
| { | ||||
|  |  | |||
|  | @ -538,6 +538,7 @@ public: | |||
|     void update_for_history_step_application(JS::NonnullGCPtr<HTML::SessionHistoryEntry>, bool do_not_reactive, size_t script_history_length, size_t script_history_index); | ||||
| 
 | ||||
|     HashMap<AK::URL, JS::GCPtr<HTML::SharedImageRequest>>& shared_image_requests(); | ||||
|     void restore_the_history_object_state(JS::NonnullGCPtr<HTML::SessionHistoryEntry> entry); | ||||
| 
 | ||||
|     JS::NonnullGCPtr<Animations::DocumentTimeline> timeline(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -148,7 +148,7 @@ WebIDL::ExceptionOr<void> Navigation::update_current_entry(NavigationUpdateCurre | |||
|     NavigationCurrentEntryChangeEventInit event_init = {}; | ||||
|     event_init.navigation_type = {}; | ||||
|     event_init.from = current; | ||||
|     dispatch_event(HTML::NavigationCurrentEntryChangeEvent::create(realm(), HTML::EventNames::currententrychange, event_init)); | ||||
|     dispatch_event(HTML::NavigationCurrentEntryChangeEvent::construct_impl(realm(), HTML::EventNames::currententrychange, event_init)); | ||||
| 
 | ||||
|     return {}; | ||||
| } | ||||
|  | @ -868,6 +868,28 @@ void Navigation::reject_the_finished_promise(JS::NonnullGCPtr<NavigationAPIMetho | |||
|     clean_up(api_method_tracker); | ||||
| } | ||||
| 
 | ||||
| // https://html.spec.whatwg.org/multipage/nav-history-apis.html#notify-about-the-committed-to-entry
 | ||||
| void Navigation::notify_about_the_committed_to_entry(JS::NonnullGCPtr<NavigationAPIMethodTracker> api_method_tracker, JS::NonnullGCPtr<NavigationHistoryEntry> nhe) | ||||
| { | ||||
|     auto& realm = this->realm(); | ||||
| 
 | ||||
|     // 1. Set apiMethodTracker's committed-to entry to nhe.
 | ||||
|     api_method_tracker->commited_to_entry = nhe; | ||||
| 
 | ||||
|     // 2. If apiMethodTracker's serialized state is not null, then set nhe's session history entry's navigation API state to apiMethodTracker's serialized state.'
 | ||||
|     // NOTE: If it's null, then we're traversing to nhe via navigation.traverseTo(), which does not allow changing the state.
 | ||||
|     if (api_method_tracker->serialized_state.has_value()) { | ||||
|         // NOTE: At this point, apiMethodTracker's serialized state is no longer needed.
 | ||||
|         //       Implementations might want to clear it out to avoid keeping it alive for the lifetime of the navigation API method tracker.
 | ||||
|         nhe->session_history_entry().navigation_api_state = *api_method_tracker->serialized_state; | ||||
|         api_method_tracker->serialized_state = {}; | ||||
|     } | ||||
| 
 | ||||
|     // 3. Resolve apiMethodTracker's committed promise with nhe.
 | ||||
|     TemporaryExecutionContext execution_context { relevant_settings_object(*this) }; | ||||
|     WebIDL::resolve_promise(realm, api_method_tracker->committed_promise, nhe); | ||||
| } | ||||
| 
 | ||||
| // https://html.spec.whatwg.org/multipage/nav-history-apis.html#inner-navigate-event-firing-algorithm
 | ||||
| bool Navigation::inner_navigate_event_firing_algorithm( | ||||
|     Bindings::NavigationType navigation_type, | ||||
|  | @ -1327,4 +1349,134 @@ bool Navigation::fire_a_download_request_navigate_event(AK::URL destination_url, | |||
|     return inner_navigate_event_firing_algorithm(Bindings::NavigationType::Push, destination, user_involvement, {}, move(filename), {}); | ||||
| } | ||||
| 
 | ||||
| // https://html.spec.whatwg.org/multipage/nav-history-apis.html#initialize-the-navigation-api-entries-for-a-new-document
 | ||||
| void Navigation::initialize_the_navigation_api_entries_for_a_new_document(Vector<JS::NonnullGCPtr<SessionHistoryEntry>> const& new_shes, JS::NonnullGCPtr<SessionHistoryEntry> initial_she) | ||||
| { | ||||
|     auto& realm = relevant_realm(*this); | ||||
| 
 | ||||
|     // 1. Assert: navigation's entry list is empty.
 | ||||
|     VERIFY(m_entry_list.is_empty()); | ||||
| 
 | ||||
|     // 2. Assert: navigation's current entry index is −1.
 | ||||
|     VERIFY(m_current_entry_index == -1); | ||||
| 
 | ||||
|     // 3. If navigation has entries and events disabled, then return.
 | ||||
|     if (has_entries_and_events_disabled()) | ||||
|         return; | ||||
| 
 | ||||
|     // 4. For each newSHE of newSHEs:
 | ||||
|     for (auto const& new_she : new_shes) { | ||||
|         // 1. Let newNHE be a new NavigationHistoryEntry created in the relevant realm of navigation.
 | ||||
|         // 2. Set newNHE's session history entry to newSHE.
 | ||||
|         auto new_nhe = NavigationHistoryEntry::create(realm, new_she); | ||||
| 
 | ||||
|         // 3. Append newNHE to navigation's entry list.
 | ||||
|         m_entry_list.append(new_nhe); | ||||
|     } | ||||
| 
 | ||||
|     // 5. Set navigation's current entry index to the result of getting the navigation API entry index of initialSHE within navigation.
 | ||||
|     m_current_entry_index = get_the_navigation_api_entry_index(*initial_she); | ||||
| } | ||||
| 
 | ||||
| // https://html.spec.whatwg.org/multipage/nav-history-apis.html#update-the-navigation-api-entries-for-a-same-document-navigation
 | ||||
| void Navigation::update_the_navigation_api_entries_for_a_same_document_navigation(JS::NonnullGCPtr<SessionHistoryEntry> destination_she, Bindings::NavigationType navigation_type) | ||||
| { | ||||
|     auto& realm = relevant_realm(*this); | ||||
| 
 | ||||
|     // 1. If navigation has entries and events disabled, then return.
 | ||||
|     if (has_entries_and_events_disabled()) | ||||
|         return; | ||||
| 
 | ||||
|     // 2. Let oldCurrentNHE be the current entry of navigation.
 | ||||
|     auto old_current_nhe = current_entry(); | ||||
| 
 | ||||
|     // 3. Let disposedNHEs be a new empty list.
 | ||||
|     Vector<JS::NonnullGCPtr<NavigationHistoryEntry>> disposed_nhes; | ||||
| 
 | ||||
|     // 4. If navigationType is "traverse", then:
 | ||||
|     if (navigation_type == Bindings::NavigationType::Traverse) { | ||||
|         // 1. Set navigation's current entry index to the result of getting the navigation API entry index of destinationSHE within navigation.
 | ||||
|         m_current_entry_index = get_the_navigation_api_entry_index(destination_she); | ||||
| 
 | ||||
|         // 2. Assert: navigation's current entry index is not −1.
 | ||||
|         // NOTE: This algorithm is only called for same-document traversals.
 | ||||
|         //       Cross-document traversals will instead call either initialize the navigation API entries for a new document
 | ||||
|         //       or update the navigation API entries for reactivation
 | ||||
|         VERIFY(m_current_entry_index != -1); | ||||
|     } | ||||
| 
 | ||||
|     // 5. Otherwise, if navigationType is "push", then:
 | ||||
|     else if (navigation_type == Bindings::NavigationType::Push) { | ||||
|         // 1. Set navigation's current entry index to navigation's current entry index + 1.
 | ||||
|         m_current_entry_index++; | ||||
| 
 | ||||
|         // 2. Let i be navigation's current entry index.
 | ||||
|         auto i = m_current_entry_index; | ||||
| 
 | ||||
|         // 3. While i < navigation's entry list's size:
 | ||||
|         while (i < static_cast<i64>(m_entry_list.size())) { | ||||
|             // 1. Append navigation's entry list[i] to disposedNHEs.
 | ||||
|             disposed_nhes.append(m_entry_list[i]); | ||||
| 
 | ||||
|             // 2. Set i to i + 1.
 | ||||
|             ++i; | ||||
|         } | ||||
| 
 | ||||
|         // 4. Remove all items in disposedNHEs from navigation's entry list.
 | ||||
|         m_entry_list.remove(m_current_entry_index, m_entry_list.size() - m_current_entry_index); | ||||
|     } | ||||
| 
 | ||||
|     // 6. Otherwise, if navigationType is "replace", then:
 | ||||
|     else if (navigation_type == Bindings::NavigationType::Replace) { | ||||
|         VERIFY(old_current_nhe != nullptr); | ||||
| 
 | ||||
|         // 1. Append oldCurrentNHE to disposedNHEs.
 | ||||
|         disposed_nhes.append(*old_current_nhe); | ||||
|     } | ||||
| 
 | ||||
|     // 7. If navigationType is "push" or "replace", then:
 | ||||
|     if (navigation_type == Bindings::NavigationType::Push || navigation_type == Bindings::NavigationType::Replace) { | ||||
|         // 1. Let newNHE be a new NavigationHistoryEntry created in the relevant realm of navigation.
 | ||||
|         // 2. Set newNHE's session history entry to destinationSHE.
 | ||||
|         auto new_nhe = NavigationHistoryEntry::create(realm, destination_she); | ||||
| 
 | ||||
|         VERIFY(m_current_entry_index != -1); | ||||
| 
 | ||||
|         // 3. Set navigation's entry list[navigation's current entry index] to newNHE.
 | ||||
|         if (m_current_entry_index < static_cast<i64>(m_entry_list.size())) | ||||
|             m_entry_list[m_current_entry_index] = new_nhe; | ||||
|         else { | ||||
|             VERIFY(m_current_entry_index == static_cast<i64>(m_entry_list.size())); | ||||
|             m_entry_list.append(new_nhe); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // 8. If navigation's ongoing API method tracker is non-null, then notify about the committed-to entry
 | ||||
|     //    given navigation's ongoing API method tracker and the current entry of navigation.
 | ||||
|     // NOTE: It is important to do this before firing the dispose or currententrychange events,
 | ||||
|     //       since event handlers could start another navigation, or otherwise change the value of
 | ||||
|     //       navigation's ongoing API method tracker.
 | ||||
|     if (m_ongoing_api_method_tracker != nullptr) | ||||
|         notify_about_the_committed_to_entry(*m_ongoing_api_method_tracker, *current_entry()); | ||||
| 
 | ||||
|     // 9. Prepare to run script given navigation's relevant settings object.
 | ||||
|     relevant_settings_object(*this).prepare_to_run_script(); | ||||
| 
 | ||||
|     // 10. Fire an event named currententrychange at navigation using NavigationCurrentEntryChangeEvent,
 | ||||
|     //     with its navigationType attribute initialized to navigationType and its from initialized to oldCurrentNHE.
 | ||||
|     NavigationCurrentEntryChangeEventInit event_init = {}; | ||||
|     event_init.navigation_type = navigation_type; | ||||
|     event_init.from = old_current_nhe; | ||||
|     dispatch_event(NavigationCurrentEntryChangeEvent::construct_impl(realm, EventNames::currententrychange, event_init)); | ||||
| 
 | ||||
|     // 11. For each disposedNHE of disposedNHEs:
 | ||||
|     for (auto& disposed_nhe : disposed_nhes) { | ||||
|         // 1. Fire an event named dispose at disposedNHE.
 | ||||
|         disposed_nhe->dispatch_event(DOM::Event::create(realm, EventNames::dispose, {})); | ||||
|     } | ||||
| 
 | ||||
|     // 12. Clean up after running script given navigation's relevant settings object.
 | ||||
|     relevant_settings_object(*this).clean_up_after_running_script(); | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -121,6 +121,9 @@ public: | |||
|         Optional<SerializationRecord> classic_history_api_state = {}); | ||||
|     bool fire_a_download_request_navigate_event(AK::URL destination_url, UserNavigationInvolvement user_involvement, String filename); | ||||
| 
 | ||||
|     void initialize_the_navigation_api_entries_for_a_new_document(Vector<JS::NonnullGCPtr<SessionHistoryEntry>> const& new_shes, JS::NonnullGCPtr<SessionHistoryEntry> initial_she); | ||||
|     void update_the_navigation_api_entries_for_a_same_document_navigation(JS::NonnullGCPtr<SessionHistoryEntry> destination_she, Bindings::NavigationType); | ||||
| 
 | ||||
|     virtual ~Navigation() override; | ||||
| 
 | ||||
|     // Internal Getters/Setters
 | ||||
|  | @ -145,6 +148,7 @@ private: | |||
|     void resolve_the_finished_promise(JS::NonnullGCPtr<NavigationAPIMethodTracker>); | ||||
|     void reject_the_finished_promise(JS::NonnullGCPtr<NavigationAPIMethodTracker>, JS::Value exception); | ||||
|     void clean_up(JS::NonnullGCPtr<NavigationAPIMethodTracker>); | ||||
|     void notify_about_the_committed_to_entry(JS::NonnullGCPtr<NavigationAPIMethodTracker>, JS::NonnullGCPtr<NavigationHistoryEntry>); | ||||
| 
 | ||||
|     bool inner_navigate_event_firing_algorithm( | ||||
|         Bindings::NavigationType, | ||||
|  |  | |||
|  | @ -472,6 +472,77 @@ void TraversableNavigable::apply_the_history_step(int step, Optional<SourceSnaps | |||
|     m_current_session_history_step = target_step; | ||||
| } | ||||
| 
 | ||||
| Vector<JS::NonnullGCPtr<SessionHistoryEntry>> TraversableNavigable::get_session_history_entries_for_the_navigation_api(JS::NonnullGCPtr<Navigable> navigable, int target_step) | ||||
| { | ||||
|     // 1. Let rawEntries be the result of getting session history entries for navigable.
 | ||||
|     auto raw_entries = navigable->get_session_history_entries(); | ||||
| 
 | ||||
|     if (raw_entries.is_empty()) | ||||
|         return {}; | ||||
| 
 | ||||
|     // 2. Let entriesForNavigationAPI be a new empty list.
 | ||||
|     Vector<JS::NonnullGCPtr<SessionHistoryEntry>> entries_for_navigation_api; | ||||
| 
 | ||||
|     // 3. Let startingIndex be the index of the session history entry in rawEntries who has the greatest step less than or equal to targetStep.
 | ||||
|     // FIXME: Use min/max_element algorithm or some such here
 | ||||
|     int starting_index = 0; | ||||
|     auto max_step = 0; | ||||
|     for (auto i = 0u; i < raw_entries.size(); ++i) { | ||||
|         auto const& entry = raw_entries[i]; | ||||
|         if (entry->step.has<int>()) { | ||||
|             auto step = entry->step.get<int>(); | ||||
|             if (step <= target_step && step > max_step) { | ||||
|                 starting_index = static_cast<int>(i); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // 4. Append rawEntries[startingIndex] to entriesForNavigationAPI.
 | ||||
|     entries_for_navigation_api.append(raw_entries[starting_index]); | ||||
| 
 | ||||
|     // 5. Let startingOrigin be rawEntries[startingIndex]'s document state's origin.
 | ||||
|     auto starting_origin = raw_entries[starting_index]->document_state->origin(); | ||||
| 
 | ||||
|     // 6. Let i be startingIndex − 1.
 | ||||
|     auto i = starting_index - 1; | ||||
| 
 | ||||
|     // 7. While i > 0:
 | ||||
|     while (i > 0) { | ||||
|         auto& entry = raw_entries[static_cast<unsigned>(i)]; | ||||
|         // 1. If rawEntries[i]'s document state's origin is not same origin with startingOrigin, then break.
 | ||||
|         auto entry_origin = entry->document_state->origin(); | ||||
|         if (starting_origin.has_value() && entry_origin.has_value() && !entry_origin->is_same_origin(*starting_origin)) | ||||
|             break; | ||||
| 
 | ||||
|         // 2. Prepend rawEntries[i] to entriesForNavigationAPI.
 | ||||
|         entries_for_navigation_api.prepend(entry); | ||||
| 
 | ||||
|         // 3. Set i to i − 1.
 | ||||
|         --i; | ||||
|     } | ||||
| 
 | ||||
|     // 8. Set i to startingIndex + 1.
 | ||||
|     i = starting_index + 1; | ||||
| 
 | ||||
|     // 9. While i < rawEntries's size:
 | ||||
|     while (i < static_cast<int>(raw_entries.size())) { | ||||
|         auto& entry = raw_entries[static_cast<unsigned>(i)]; | ||||
|         // 1. If rawEntries[i]'s document state's origin is not same origin with startingOrigin, then break.
 | ||||
|         auto entry_origin = entry->document_state->origin(); | ||||
|         if (starting_origin.has_value() && entry_origin.has_value() && !entry_origin->is_same_origin(*starting_origin)) | ||||
|             break; | ||||
| 
 | ||||
|         // 2. Append rawEntries[i] to entriesForNavigationAPI.
 | ||||
|         entries_for_navigation_api.append(entry); | ||||
| 
 | ||||
|         // 3. Set i to i + 1.
 | ||||
|         ++i; | ||||
|     } | ||||
| 
 | ||||
|     // 10. Return entriesForNavigationAPI.
 | ||||
|     return entries_for_navigation_api; | ||||
| } | ||||
| 
 | ||||
| // https://html.spec.whatwg.org/multipage/browsing-the-web.html#clear-the-forward-session-history
 | ||||
| void TraversableNavigable::clear_the_forward_session_history() | ||||
| { | ||||
|  |  | |||
|  | @ -73,6 +73,8 @@ private: | |||
| 
 | ||||
|     void apply_the_history_step(int step, Optional<SourceSnapshotParams> = {}); | ||||
| 
 | ||||
|     Vector<JS::NonnullGCPtr<SessionHistoryEntry>> get_session_history_entries_for_the_navigation_api(JS::NonnullGCPtr<Navigable>, int); | ||||
| 
 | ||||
|     // https://html.spec.whatwg.org/multipage/document-sequences.html#tn-current-session-history-step
 | ||||
|     int m_current_session_history_step { 0 }; | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andrew Kaster
						Andrew Kaster