mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2026-06-17 15:25:35 +00:00
Add a browser-side model for top-level history entries and history step coordinates. This gives the UI process a structure to mirror WebContent history across process swaps. Add debug dumping support alongside the model so traversal state can be inspected while working on back and forward behavior.
116 lines
5.5 KiB
C++
116 lines
5.5 KiB
C++
/*
|
|
* Copyright (c) 2026-present, the Ladybird developers.
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <AK/Optional.h>
|
|
#include <AK/Vector.h>
|
|
#include <LibWeb/HTML/SessionHistoryEntry.h>
|
|
#include <LibWebView/Export.h>
|
|
|
|
namespace WebView {
|
|
|
|
// AD-HOC: The HTML Standard stores a traversable navigable's session history entries on the traversable. Ladybird
|
|
// keeps an IPC-serializable mirror in the UI process so browser history survives WebContent process swaps
|
|
// and crash recovery. The mirror still uses the spec's session history entry and all used history steps model.
|
|
//
|
|
// https://html.spec.whatwg.org/multipage/document-sequences.html#tn-session-history-entries
|
|
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#getting-all-used-history-steps
|
|
class WEBVIEW_API TraversableSessionHistory {
|
|
public:
|
|
using Entry = Web::HTML::SessionHistoryEntryDescriptor;
|
|
|
|
struct TraversalTarget {
|
|
size_t target_step_index { 0 };
|
|
i32 target_step { 0 };
|
|
Entry const* target_top_level_entry { nullptr };
|
|
bool target_step_is_top_level_entry { false };
|
|
bool changes_top_level_entry { false };
|
|
};
|
|
|
|
enum class UpdateResult {
|
|
// WebContent sent the same complete top-level traversable session
|
|
// history that the UI process stores authoritatively.
|
|
CompleteSnapshot,
|
|
|
|
// WebContent sent a valid partial view of session history. The UI
|
|
// process merged it into the authoritative history mirror, but
|
|
// WebContent cannot be assumed to know or use every UI history step.
|
|
MergedPartialSnapshot,
|
|
|
|
// WebContent sent a snapshot that cannot describe the current UI-owned
|
|
// traversable session history.
|
|
InvalidSnapshot,
|
|
};
|
|
|
|
bool is_empty() const { return m_entries.is_empty(); }
|
|
size_t size() const { return m_entries.size(); }
|
|
size_t used_step_count() const { return m_used_steps.size(); }
|
|
Optional<size_t> current_used_step_index() const { return m_current_used_step_index; }
|
|
Optional<size_t> current_top_level_entry_index() const;
|
|
|
|
void clear();
|
|
void navigate(URL::URL);
|
|
void navigate(URL::URL, Variant<Empty, String, Web::HTML::POSTResource>);
|
|
void replace_current_entry_url(URL::URL);
|
|
void replace_current_entry(URL::URL, Variant<Empty, String, Web::HTML::POSTResource>);
|
|
void mark_current_entry_reload_pending();
|
|
void clear_current_entry_reload_pending();
|
|
UpdateResult update_from_web_content(Vector<Entry> entries, Vector<i32> used_steps, size_t current_used_step_index);
|
|
[[nodiscard]] bool did_seed_web_content_from_ui_process(Vector<Entry> entries, Vector<i32> used_steps, size_t current_used_step_index);
|
|
void did_seed_web_content_from_ui_process(size_t current_top_level_entry_index);
|
|
[[nodiscard]] bool did_restore_web_content_to_current_step(i32 step);
|
|
[[nodiscard]] bool did_apply_web_content_traversal_to_step(i32 step);
|
|
void forget_web_content_state();
|
|
Vector<Entry> entries() const;
|
|
Vector<i32> used_steps() const;
|
|
Vector<Entry> web_content_known_entries() const;
|
|
Vector<i32> web_content_known_used_steps() const;
|
|
Optional<i32> web_content_current_step() const;
|
|
bool web_content_uses_ui_step_coordinates() const { return m_web_content_uses_ui_step_coordinates; }
|
|
bool web_content_history_matches_mirror() const;
|
|
|
|
[[nodiscard]] bool can_go_back() const;
|
|
[[nodiscard]] bool can_go_forward() const;
|
|
[[nodiscard]] bool has_only_top_level_used_steps() const;
|
|
[[nodiscard]] bool current_step_is_top_level_entry() const;
|
|
[[nodiscard]] Optional<i32> current_step_to_restore_after_loading_top_level_entry() const;
|
|
[[nodiscard]] bool web_content_can_traverse_to(TraversalTarget const&) const;
|
|
[[nodiscard]] Optional<TraversalTarget> traversal_target_for_delta(int delta) const;
|
|
[[nodiscard]] Optional<TraversalTarget> traversal_target_for_step(i32 step) const;
|
|
[[nodiscard]] Optional<size_t> target_step_index_for_delta(int delta) const;
|
|
[[nodiscard]] Optional<i32> step_at(size_t index) const;
|
|
[[nodiscard]] Entry const* current_entry() const;
|
|
[[nodiscard]] Entry const* entry_at(size_t index) const;
|
|
[[nodiscard]] Entry const* entry_for_step(i32 step) const;
|
|
[[nodiscard]] Entry const* top_level_entry_for_step(i32 step) const;
|
|
|
|
void traverse_to(size_t index);
|
|
|
|
private:
|
|
// https://html.spec.whatwg.org/multipage/document-sequences.html#tn-session-history-entries
|
|
Vector<Entry> m_entries;
|
|
|
|
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#getting-all-used-history-steps
|
|
Vector<i32> m_used_steps;
|
|
|
|
// Index of the current session history step within m_used_steps.
|
|
// https://html.spec.whatwg.org/multipage/document-sequences.html#tn-current-session-history-step
|
|
Optional<size_t> m_current_used_step_index;
|
|
|
|
// WebContent's latest current session history step, translated into the
|
|
// UI-owned traversable session history's step coordinate space.
|
|
// https://html.spec.whatwg.org/multipage/document-sequences.html#tn-current-session-history-step
|
|
Vector<Entry> m_web_content_known_entries;
|
|
Vector<i32> m_web_content_known_used_steps;
|
|
Optional<i32> m_web_content_current_step;
|
|
// False when a partial snapshot was translated into the UI-owned step
|
|
// coordinate space. In that state WebContent still uses its original step
|
|
// numbers, so the UI must reseed/load instead of delegating traversal by step.
|
|
bool m_web_content_uses_ui_step_coordinates { false };
|
|
};
|
|
|
|
}
|