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