diff --git a/Userland/Libraries/LibWeb/HTML/Navigable.cpp b/Userland/Libraries/LibWeb/HTML/Navigable.cpp index 898768de4c..84bd71e329 100644 --- a/Userland/Libraries/LibWeb/HTML/Navigable.cpp +++ b/Userland/Libraries/LibWeb/HTML/Navigable.cpp @@ -939,61 +939,64 @@ WebIDL::ExceptionOr Navigable::navigate( // for historyEntry, given navigable, "navigate", sourceSnapshotParams, // targetSnapshotParams, navigationId, navigationParams, cspNavigationType, with allowPOST // set to true and completionSteps set to the following step: - populate_session_history_entry_document(history_entry, navigation_params, navigation_id, source_snapshot_params, [this, history_entry, history_handling] { - // https://html.spec.whatwg.org/multipage/browsing-the-web.html#finalize-a-cross-document-navigation + populate_session_history_entry_document(history_entry, navigation_params, navigation_id, source_snapshot_params, [this, history_entry, history_handling, navigation_id] { + traversable_navigable()->append_session_history_traversal_steps([this, history_entry, history_handling, navigation_id] { + // https://html.spec.whatwg.org/multipage/browsing-the-web.html#finalize-a-cross-document-navigation - // 1. FIXME: Assert: this is running on navigable's traversable navigable's session history traversal queue. + // 1. FIXME: Assert: this is running on navigable's traversable navigable's session history traversal queue. - // 2. Set navigable's is delaying load events to false. - set_delaying_load_events(false); + // 2. Set navigable's is delaying load events to false. + set_delaying_load_events(false); - // 3. If historyEntry's document is null, then return. - if (!history_entry->document_state->document()) - return; + // 3. If historyEntry's document is null, then return. + if (!history_entry->document_state->document()) + return; - // 4. FIXME: If all of the following are true: - // - navigable's parent is null; - // - historyEntry's document's browsing context is not an auxiliary browsing context whose opener browsing context is non-null; and - // - historyEntry's document's origin is not navigable's active document's origin - // then set historyEntry's document state's navigable target name to the empty string. + // 4. FIXME: If all of the following are true: + // - navigable's parent is null; + // - historyEntry's document's browsing context is not an auxiliary browsing context whose opener browsing context is non-null; and + // - historyEntry's document's origin is not navigable's active document's origin + // then set historyEntry's document state's navigable target name to the empty string. - // 5. Let entryToReplace be navigable's active session history entry if historyHandling is "replace", otherwise null. - auto entry_to_replace = history_handling == HistoryHandlingBehavior::Replace ? active_session_history_entry() : nullptr; + // 5. Let entryToReplace be navigable's active session history entry if historyHandling is "replace", otherwise null. + auto entry_to_replace = history_handling == HistoryHandlingBehavior::Replace ? active_session_history_entry() : nullptr; - // 6. Let traversable be navigable's traversable navigable. - auto traversable = traversable_navigable(); + // 6. Let traversable be navigable's traversable navigable. + auto traversable = traversable_navigable(); - // 7. Let targetStep be null. - int target_step; + // 7. Let targetStep be null. + int target_step; - // 8. Let targetEntries be the result of getting session history entries for navigable. - auto& target_entries = get_session_history_entries(); + // 8. Let targetEntries be the result of getting session history entries for navigable. + auto& target_entries = get_session_history_entries(); - // 9. If entryToReplace is null, then: - if (entry_to_replace == nullptr) { - // FIXME: 1. Clear the forward session history of traversable. + // 9. If entryToReplace is null, then: + if (entry_to_replace == nullptr) { + // FIXME: 1. Clear the forward session history of traversable. + traversable->clear_the_forward_session_history(); - // 2. Set targetStep to traversable's current session history step + 1. - target_step = traversable->current_session_history_step() + 1; + // 2. Set targetStep to traversable's current session history step + 1. + target_step = traversable->current_session_history_step() + 1; - // 3. Set historyEntry's step to targetStep. - history_entry->step = target_step; + // 3. Set historyEntry's step to targetStep. + history_entry->step = target_step; - // 4. Append historyEntry to targetEntries. - target_entries.append(move(history_entry)); - } else { - // 1. Replace entryToReplace with historyEntry in targetEntries. - *(target_entries.find(*entry_to_replace)) = history_entry; + // 4. Append historyEntry to targetEntries. + target_entries.append(move(history_entry)); + } else { + // 1. Replace entryToReplace with historyEntry in targetEntries. + *(target_entries.find(*entry_to_replace)) = history_entry; - // 2. Set historyEntry's step to entryToReplace's step. - history_entry->step = entry_to_replace->step; + // 2. Set historyEntry's step to entryToReplace's step. + history_entry->step = entry_to_replace->step; - // 3. Set targetStep to traversable's current session history step. - target_step = traversable->current_session_history_step(); - } + // 3. Set targetStep to traversable's current session history step. + target_step = traversable->current_session_history_step(); + } - // 10. Apply the history step targetStep to traversable. - traversable->apply_the_history_step(target_step); + // 10. Apply the history step targetStep to traversable. + traversable->apply_the_history_step(target_step); + }); }).release_value_but_fixme_should_propagate_errors(); }); @@ -1022,10 +1025,11 @@ void Navigable::reload() // 2. Let traversable be navigable's traversable navigable. auto traversable = traversable_navigable(); - // FIXME: 3. Append the following session history traversal steps to traversable: - - // 1. Apply pending history changes to traversable with true. - traversable->apply_pending_history_changes(); + // 3. Append the following session history traversal steps to traversable: + traversable->append_session_history_traversal_steps([traversable] { + // 1. Apply pending history changes to traversable with true. + traversable->apply_pending_history_changes(); + }); } } diff --git a/Userland/Libraries/LibWeb/HTML/NavigableContainer.cpp b/Userland/Libraries/LibWeb/HTML/NavigableContainer.cpp index 4f4d1b0488..cc4197f478 100644 --- a/Userland/Libraries/LibWeb/HTML/NavigableContainer.cpp +++ b/Userland/Libraries/LibWeb/HTML/NavigableContainer.cpp @@ -102,30 +102,31 @@ WebIDL::ExceptionOr NavigableContainer::create_new_child_navigable() // 11. Let traversable be parentNavigable's traversable navigable. auto traversable = parent_navigable->traversable_navigable(); - // FIXME: 12. Append the following session history traversal steps to traversable: + // 12. Append the following session history traversal steps to traversable: + traversable->append_session_history_traversal_steps([traversable, navigable, parent_navigable, history_entry] { + // 1. Let parentDocState be parentNavigable's active session history entry's document state. - // 1. Let parentDocState be parentNavigable's active session history entry's document state. + auto parent_doc_state = parent_navigable->active_session_history_entry()->document_state; - auto parent_doc_state = parent_navigable->active_session_history_entry()->document_state; + // 2. Let targetStepSHE be the first session history entry in traversable's session history entries whose document state equals parentDocState. + auto target_step_she = *(traversable->session_history_entries().find_if([parent_doc_state](auto& entry) { + return entry->document_state == parent_doc_state; + })); - // 2. Let targetStepSHE be the first session history entry in traversable's session history entries whose document state equals parentDocState. - auto target_step_she = *(traversable->session_history_entries().find_if([parent_doc_state](auto& entry) { - return entry->document_state == parent_doc_state; - })); + // 3. Set historyEntry's step to targetStepSHE's step. + history_entry->step = target_step_she->step; - // 3. Set historyEntry's step to targetStepSHE's step. - history_entry->step = target_step_she->step; + // 4. Let nestedHistory be a new nested history whose id is navigable's id and entries list is « historyEntry ». + DocumentState::NestedHistory nested_history { + .id = navigable->id(), + .entries { *history_entry }, + }; - // 4. Let nestedHistory be a new nested history whose id is navigable's id and entries list is « historyEntry ». - DocumentState::NestedHistory nested_history { - .id = navigable->id(), - .entries { *history_entry }, - }; + // 5. Append nestedHistory to parentDocState's nested histories. + parent_doc_state->nested_histories().append(move(nested_history)); - // 5. Append nestedHistory to parentDocState's nested histories. - parent_doc_state->nested_histories().append(move(nested_history)); - - // FIXME: 6. Update for navigable creation/destruction given traversable + // FIXME: 6. Update for navigable creation/destruction given traversable + }); return {}; } @@ -342,10 +343,11 @@ void NavigableContainer::destroy_the_child_navigable() // 7. Let traversable be container's node navigable's traversable navigable. auto traversable = this->navigable()->traversable_navigable(); - // FIXME: 8. Append the following session history traversal steps to traversable: - - // 1. Apply pending history changes to traversable. - traversable->apply_pending_history_changes(); + // 8. Append the following session history traversal steps to traversable: + traversable->append_session_history_traversal_steps([traversable] { + // 1. Apply pending history changes to traversable. + traversable->apply_pending_history_changes(); + }); } } diff --git a/Userland/Libraries/LibWeb/HTML/SessionHistoryTraversalQueue.h b/Userland/Libraries/LibWeb/HTML/SessionHistoryTraversalQueue.h new file mode 100644 index 0000000000..c2cc7f8b88 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/SessionHistoryTraversalQueue.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023, Aliaksandr Kalenik + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace Web::HTML { + +// https://html.spec.whatwg.org/multipage/document-sequences.html#tn-session-history-traversal-queue +class SessionHistoryTraversalQueue { +public: + SessionHistoryTraversalQueue() + { + m_timer = Core::Timer::create_single_shot(0, [this] { + while (m_queue.size() > 0) { + auto steps = m_queue.take_first(); + steps(); + } + }).release_value_but_fixme_should_propagate_errors(); + } + + void append(JS::SafeFunction steps) + { + m_queue.append(move(steps)); + if (!m_timer->is_active()) { + m_timer->start(); + } + } + +private: + Vector> m_queue; + RefPtr m_timer; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/TraversableNavigable.cpp b/Userland/Libraries/LibWeb/HTML/TraversableNavigable.cpp index 19b76e2329..822d1a65d1 100644 --- a/Userland/Libraries/LibWeb/HTML/TraversableNavigable.cpp +++ b/Userland/Libraries/LibWeb/HTML/TraversableNavigable.cpp @@ -479,24 +479,25 @@ void TraversableNavigable::traverse_the_history_by_delta(int delta) // FIXME: 2. If sourceDocument is given, then: // 3. Append the following session history traversal steps to traversable: + append_session_history_traversal_steps([this, delta] { + // 1. Let allSteps be the result of getting all used history steps for traversable. + auto all_steps = get_all_used_history_steps(); - // 1. Let allSteps be the result of getting all used history steps for traversable. - auto all_steps = get_all_used_history_steps(); + // 2. Let currentStepIndex be the index of traversable's current session history step within allSteps. + auto current_step_index = *all_steps.find_first_index(current_session_history_step()); - // 2. Let currentStepIndex be the index of traversable's current session history step within allSteps. - auto current_step_index = *all_steps.find_first_index(current_session_history_step()); + // 3. Let targetStepIndex be currentStepIndex plus delta + auto target_step_index = current_step_index + delta; - // 3. Let targetStepIndex be currentStepIndex plus delta - auto target_step_index = current_step_index + delta; + // 4. If allSteps[targetStepIndex] does not exist, then abort these steps. + if (target_step_index >= all_steps.size()) { + return; + } - // 4. If allSteps[targetStepIndex] does not exist, then abort these steps. - if (target_step_index >= all_steps.size()) { - return; - } - - // 5. Apply the history step allSteps[targetStepIndex] to traversable, with checkForUserCancelation set to true, - // sourceSnapshotParams set to sourceSnapshotParams, and initiatorToCheck set to initiatorToCheck. - apply_the_history_step(all_steps[target_step_index]); + // 5. Apply the history step allSteps[targetStepIndex] to traversable, with checkForUserCancelation set to true, + // sourceSnapshotParams set to sourceSnapshotParams, and initiatorToCheck set to initiatorToCheck. + apply_the_history_step(all_steps[target_step_index]); + }); } // https://html.spec.whatwg.org/multipage/browsing-the-web.html#apply-pending-history-changes diff --git a/Userland/Libraries/LibWeb/HTML/TraversableNavigable.h b/Userland/Libraries/LibWeb/HTML/TraversableNavigable.h index 4e45987906..ce237407e4 100644 --- a/Userland/Libraries/LibWeb/HTML/TraversableNavigable.h +++ b/Userland/Libraries/LibWeb/HTML/TraversableNavigable.h @@ -8,6 +8,7 @@ #include #include +#include #include namespace Web::HTML { @@ -47,6 +48,11 @@ public: void destroy_top_level_traversable(); + void append_session_history_traversal_steps(JS::SafeFunction steps) + { + m_session_history_traversal_queue.append(move(steps)); + } + private: TraversableNavigable(); @@ -65,6 +71,8 @@ private: // https://html.spec.whatwg.org/multipage/document-sequences.html#system-visibility-state VisibilityState m_system_visibility_state { VisibilityState::Visible }; + + SessionHistoryTraversalQueue m_session_history_traversal_queue; }; }