mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2026-06-23 01:40:26 +00:00
Treat pending session history entries as absent from the used step graph, and share that through a small step_value() helper so snapshotting, Navigation API entry construction, target-entry lookup, and forward clearing do not drift apart. Keep cross-document history application tied to the navigation id that created it. Queued changing-navigable work now finishes without applying when a later navigation has already replaced its target, and any traversal sentinel is cleared through the shared setter so queued navigations can drain. When navigation arrives while traversal is still ongoing, keep only the newest pending navigation. This matches Chromium, WebKit, and Gecko on sites that click through product or category links while prior loads settle. Revalidate queued same-document child continuations before running them from null-document tasks, so removed frames or frames claimed by newer navigations do not receive stale history state. Preserve nested-history descriptors even when all child entries are pending, keeping live child navigable identity available for later UI process history seeds. Add regression coverage for iframe renavigation during history commit, for pending child history followed by a real navigation, and for removed iframes with queued history updates.
470 lines
21 KiB
C++
470 lines
21 KiB
C++
/*
|
|
* Copyright (c) 2022, Andreas Kling <andreas@ladybird.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <LibIPC/Decoder.h>
|
|
#include <LibIPC/Encoder.h>
|
|
#include <LibJS/Runtime/VM.h>
|
|
#include <LibWeb/Crypto/Crypto.h>
|
|
#include <LibWeb/HTML/DocumentState.h>
|
|
#include <LibWeb/HTML/SessionHistoryEntry.h>
|
|
#include <LibWeb/HTML/StructuredSerialize.h>
|
|
|
|
namespace Web::HTML {
|
|
|
|
NonnullRefPtr<SessionHistoryEntry> SessionHistoryEntry::create()
|
|
{
|
|
return adopt_ref(*new SessionHistoryEntry());
|
|
}
|
|
|
|
SessionHistoryEntry::~SessionHistoryEntry() = default;
|
|
|
|
RefPtr<DocumentState> SessionHistoryEntry::document_state() const { return m_document_state; }
|
|
void SessionHistoryEntry::set_document_state(RefPtr<DocumentState> document_state) { m_document_state = move(document_state); }
|
|
|
|
SessionHistoryEntry::SessionHistoryEntry()
|
|
: m_classic_history_api_state(MUST(structured_serialize_for_storage(JS::VM::the(), JS::js_null())))
|
|
, m_navigation_api_state(MUST(structured_serialize_for_storage(JS::VM::the(), JS::js_undefined())))
|
|
, m_navigation_api_key(Crypto::generate_random_uuid())
|
|
, m_navigation_api_id(Crypto::generate_random_uuid())
|
|
{
|
|
}
|
|
|
|
static u64 document_state_id_for_descriptor(DocumentState const& document_state, SessionHistoryEntryDescriptorCreationState& creation_state)
|
|
{
|
|
if (auto id = creation_state.document_state_ids.get(&document_state); id.has_value())
|
|
return *id;
|
|
|
|
auto id = creation_state.next_document_state_id++;
|
|
VERIFY(id != 0);
|
|
creation_state.document_state_ids.set(&document_state, id);
|
|
return id;
|
|
}
|
|
|
|
static SessionHistoryDocumentStateDescriptor create_session_history_document_state_descriptor(DocumentState const& document_state, SessionHistoryEntryDescriptorCreationState& creation_state)
|
|
{
|
|
Vector<SessionHistoryNestedHistoryDescriptor> nested_history_descriptors;
|
|
nested_history_descriptors.ensure_capacity(document_state.nested_histories().size());
|
|
for (auto const& nested_history : document_state.nested_histories()) {
|
|
Vector<SessionHistoryEntryDescriptor> nested_entry_descriptors;
|
|
nested_entry_descriptors.ensure_capacity(nested_history.entries.size());
|
|
for (auto const& nested_entry : nested_history.entries) {
|
|
// NB: UI-process session history mirrors only concrete used history steps. A child entry whose step is
|
|
// still "pending" has not been attached to the traversable's step graph yet.
|
|
if (!nested_entry->step_value().has_value())
|
|
continue;
|
|
nested_entry_descriptors.unchecked_append(create_session_history_entry_descriptor(nested_entry, creation_state));
|
|
}
|
|
|
|
// NB: Keep the nested-history descriptor even when every entry in it is still pending. The entries are not
|
|
// used history steps yet, but the descriptor id preserves the live child navigable identity when the UI
|
|
// process later reseeds an already-loaded document.
|
|
nested_history_descriptors.unchecked_append({
|
|
.id = nested_history.id,
|
|
.entries = move(nested_entry_descriptors),
|
|
});
|
|
}
|
|
|
|
return {
|
|
.id = document_state_id_for_descriptor(document_state, creation_state),
|
|
.history_policy_container = document_state.history_policy_container(),
|
|
.request_referrer = document_state.request_referrer(),
|
|
.request_referrer_policy = document_state.request_referrer_policy(),
|
|
.initiator_origin = document_state.initiator_origin(),
|
|
.origin = document_state.origin(),
|
|
.about_base_url = document_state.about_base_url(),
|
|
.resource = document_state.resource(),
|
|
.reload_pending = document_state.reload_pending(),
|
|
.ever_populated = document_state.ever_populated(),
|
|
.navigable_target_name = document_state.navigable_target_name(),
|
|
.nested_histories = move(nested_history_descriptors),
|
|
};
|
|
}
|
|
|
|
SessionHistoryEntryDescriptor create_session_history_entry_descriptor(SessionHistoryEntry const& entry, SessionHistoryEntryDescriptorCreationState& creation_state)
|
|
{
|
|
auto entry_step = entry.step_value();
|
|
VERIFY(entry_step.has_value());
|
|
SessionHistoryDocumentStateDescriptor document_state_descriptor;
|
|
if (auto document_state = entry.document_state())
|
|
document_state_descriptor = create_session_history_document_state_descriptor(*document_state, creation_state);
|
|
|
|
return {
|
|
.step = static_cast<i32>(*entry_step),
|
|
.url = entry.url(),
|
|
.document_state = move(document_state_descriptor),
|
|
.classic_history_api_state = entry.classic_history_api_state(),
|
|
.navigation_api_state = entry.navigation_api_state(),
|
|
.navigation_api_key = entry.navigation_api_key(),
|
|
.navigation_api_id = entry.navigation_api_id(),
|
|
.scroll_restoration_mode = entry.scroll_restoration_mode(),
|
|
.scroll_position_data = entry.scroll_position_data(),
|
|
};
|
|
}
|
|
|
|
static bool session_history_nested_history_descriptors_match(Vector<SessionHistoryNestedHistoryDescriptor> const& a, Vector<SessionHistoryNestedHistoryDescriptor> const& b);
|
|
|
|
static bool serialized_directives_match(Vector<ContentSecurityPolicy::Directives::SerializedDirective> const& a, Vector<ContentSecurityPolicy::Directives::SerializedDirective> const& b)
|
|
{
|
|
if (a.size() != b.size())
|
|
return false;
|
|
|
|
for (size_t i = 0; i < a.size(); ++i) {
|
|
if (a[i].name != b[i].name || a[i].value != b[i].value)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool serialized_policies_match(Vector<ContentSecurityPolicy::SerializedPolicy> const& a, Vector<ContentSecurityPolicy::SerializedPolicy> const& b)
|
|
{
|
|
if (a.size() != b.size())
|
|
return false;
|
|
|
|
for (size_t i = 0; i < a.size(); ++i) {
|
|
if (!serialized_directives_match(a[i].directives, b[i].directives)
|
|
|| a[i].disposition != b[i].disposition
|
|
|| a[i].source != b[i].source
|
|
|| a[i].self_origin != b[i].self_origin
|
|
|| a[i].pre_parsed_policy_string != b[i].pre_parsed_policy_string)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool embedder_policies_match(EmbedderPolicy const& a, EmbedderPolicy const& b)
|
|
{
|
|
return a.value == b.value
|
|
&& a.report_only_value == b.report_only_value
|
|
&& a.reporting_endpoint == b.reporting_endpoint
|
|
&& a.report_only_reporting_endpoint == b.report_only_reporting_endpoint;
|
|
}
|
|
|
|
static bool serialized_policy_containers_match(SerializedPolicyContainer const& a, SerializedPolicyContainer const& b)
|
|
{
|
|
return serialized_policies_match(a.csp_list, b.csp_list)
|
|
&& embedder_policies_match(a.embedder_policy, b.embedder_policy)
|
|
&& a.referrer_policy == b.referrer_policy;
|
|
}
|
|
|
|
static bool history_policy_containers_match(Variant<SerializedPolicyContainer, DocumentState::Client> const& a, Variant<SerializedPolicyContainer, DocumentState::Client> const& b)
|
|
{
|
|
if (auto const* a_serialized_policy_container = a.get_pointer<SerializedPolicyContainer>()) {
|
|
auto const* b_serialized_policy_container = b.get_pointer<SerializedPolicyContainer>();
|
|
return b_serialized_policy_container && serialized_policy_containers_match(*a_serialized_policy_container, *b_serialized_policy_container);
|
|
}
|
|
|
|
return a.has<DocumentState::Client>() && b.has<DocumentState::Client>();
|
|
}
|
|
|
|
static bool session_history_document_state_descriptors_match(SessionHistoryDocumentStateDescriptor const& a, SessionHistoryDocumentStateDescriptor const& b)
|
|
{
|
|
return a.id == b.id
|
|
&& history_policy_containers_match(a.history_policy_container, b.history_policy_container)
|
|
&& a.request_referrer == b.request_referrer
|
|
&& a.request_referrer_policy == b.request_referrer_policy
|
|
&& a.initiator_origin == b.initiator_origin
|
|
&& a.origin == b.origin
|
|
&& a.about_base_url == b.about_base_url
|
|
&& a.resource == b.resource
|
|
&& a.reload_pending == b.reload_pending
|
|
&& a.ever_populated == b.ever_populated
|
|
&& a.navigable_target_name == b.navigable_target_name
|
|
&& session_history_nested_history_descriptors_match(a.nested_histories, b.nested_histories);
|
|
}
|
|
|
|
static bool session_history_nested_history_descriptors_match_ignoring_document_state_ids(Vector<SessionHistoryNestedHistoryDescriptor> const&, Vector<SessionHistoryNestedHistoryDescriptor> const&);
|
|
|
|
static bool session_history_document_state_descriptors_match_ignoring_id(SessionHistoryDocumentStateDescriptor const& a, SessionHistoryDocumentStateDescriptor const& b, MatchNestedHistories match_nested_histories)
|
|
{
|
|
if (!(history_policy_containers_match(a.history_policy_container, b.history_policy_container)
|
|
&& a.request_referrer == b.request_referrer
|
|
&& a.request_referrer_policy == b.request_referrer_policy
|
|
&& a.initiator_origin == b.initiator_origin
|
|
&& a.origin == b.origin
|
|
&& a.about_base_url == b.about_base_url
|
|
&& a.resource == b.resource
|
|
&& a.reload_pending == b.reload_pending
|
|
&& a.ever_populated == b.ever_populated
|
|
&& a.navigable_target_name == b.navigable_target_name))
|
|
return false;
|
|
|
|
if (match_nested_histories == MatchNestedHistories::No)
|
|
return true;
|
|
|
|
return session_history_nested_history_descriptors_match_ignoring_document_state_ids(a.nested_histories, b.nested_histories);
|
|
}
|
|
|
|
bool session_history_entry_descriptors_match(SessionHistoryEntryDescriptor const& a, SessionHistoryEntryDescriptor const& b)
|
|
{
|
|
return a.step == b.step
|
|
&& a.url == b.url
|
|
&& session_history_document_state_descriptors_match(a.document_state, b.document_state)
|
|
&& a.classic_history_api_state == b.classic_history_api_state
|
|
&& a.navigation_api_state == b.navigation_api_state
|
|
&& a.navigation_api_key == b.navigation_api_key
|
|
&& a.navigation_api_id == b.navigation_api_id
|
|
&& a.scroll_restoration_mode == b.scroll_restoration_mode
|
|
&& a.scroll_position_data == b.scroll_position_data;
|
|
}
|
|
|
|
bool session_history_entry_descriptors_match_ignoring_document_state_id(SessionHistoryEntryDescriptor const& a, SessionHistoryEntryDescriptor const& b, MatchNestedHistories match_nested_histories)
|
|
{
|
|
if (a.step != b.step)
|
|
return false;
|
|
if (a.url != b.url)
|
|
return false;
|
|
if (a.document_state.id != 0 && !session_history_document_state_descriptors_match_ignoring_id(a.document_state, b.document_state, match_nested_histories))
|
|
return false;
|
|
if (a.classic_history_api_state != b.classic_history_api_state)
|
|
return false;
|
|
if (a.navigation_api_state != b.navigation_api_state || a.navigation_api_key != b.navigation_api_key || a.navigation_api_id != b.navigation_api_id)
|
|
return false;
|
|
if (a.scroll_restoration_mode != b.scroll_restoration_mode)
|
|
return false;
|
|
if (a.document_state.id != 0 && a.scroll_position_data != b.scroll_position_data)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
static bool session_history_entry_descriptors_match(Vector<SessionHistoryEntryDescriptor> const& a, Vector<SessionHistoryEntryDescriptor> const& b)
|
|
{
|
|
if (a.size() != b.size())
|
|
return false;
|
|
|
|
for (size_t i = 0; i < a.size(); ++i) {
|
|
if (session_history_entry_descriptors_match(a[i], b[i]))
|
|
continue;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool session_history_entry_descriptors_match_ignoring_document_state_ids(Vector<SessionHistoryEntryDescriptor> const& a, Vector<SessionHistoryEntryDescriptor> const& b)
|
|
{
|
|
if (a.size() != b.size())
|
|
return false;
|
|
|
|
for (size_t i = 0; i < a.size(); ++i) {
|
|
if (session_history_entry_descriptors_match_ignoring_document_state_id(a[i], b[i]))
|
|
continue;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool session_history_nested_history_descriptors_match(Vector<SessionHistoryNestedHistoryDescriptor> const& a, Vector<SessionHistoryNestedHistoryDescriptor> const& b)
|
|
{
|
|
if (a.size() != b.size())
|
|
return false;
|
|
|
|
for (size_t i = 0; i < a.size(); ++i) {
|
|
if (a[i].id == b[i].id && session_history_entry_descriptors_match(a[i].entries, b[i].entries))
|
|
continue;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool session_history_nested_history_descriptors_match_ignoring_document_state_ids(Vector<SessionHistoryNestedHistoryDescriptor> const& a, Vector<SessionHistoryNestedHistoryDescriptor> const& b)
|
|
{
|
|
if (a.size() != b.size())
|
|
return false;
|
|
|
|
for (size_t i = 0; i < a.size(); ++i) {
|
|
if (a[i].id == b[i].id && session_history_entry_descriptors_match_ignoring_document_state_ids(a[i].entries, b[i].entries))
|
|
continue;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool session_history_nested_history_descriptors_match_document_state_nested_histories(Vector<SessionHistoryNestedHistoryDescriptor> const& descriptors, Vector<DocumentState::NestedHistory> const& nested_histories)
|
|
{
|
|
if (descriptors.size() != nested_histories.size())
|
|
return false;
|
|
|
|
for (size_t i = 0; i < descriptors.size(); ++i) {
|
|
auto const& descriptor = descriptors[i];
|
|
auto const& nested_history = nested_histories[i];
|
|
if (descriptor.id != nested_history.id || descriptor.entries.size() != nested_history.entries.size())
|
|
return false;
|
|
for (size_t j = 0; j < descriptor.entries.size(); ++j) {
|
|
if (!session_history_entry_matches_descriptor_ignoring_document_state_id(*nested_history.entries[j], descriptor.entries[j]))
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool session_history_document_state_descriptor_matches_document_state_ignoring_id(SessionHistoryDocumentStateDescriptor const& descriptor, DocumentState const& document_state, MatchNestedHistories match_nested_histories)
|
|
{
|
|
if (!(history_policy_containers_match(descriptor.history_policy_container, document_state.history_policy_container())
|
|
&& descriptor.request_referrer == document_state.request_referrer()
|
|
&& descriptor.request_referrer_policy == document_state.request_referrer_policy()
|
|
&& descriptor.initiator_origin == document_state.initiator_origin()
|
|
&& descriptor.origin == document_state.origin()
|
|
&& descriptor.about_base_url == document_state.about_base_url()
|
|
&& descriptor.resource == document_state.resource()
|
|
&& descriptor.reload_pending == document_state.reload_pending()
|
|
&& descriptor.ever_populated == document_state.ever_populated()
|
|
&& descriptor.navigable_target_name == document_state.navigable_target_name()))
|
|
return false;
|
|
|
|
if (match_nested_histories == MatchNestedHistories::No)
|
|
return true;
|
|
|
|
return session_history_nested_history_descriptors_match_document_state_nested_histories(descriptor.nested_histories, document_state.nested_histories());
|
|
}
|
|
|
|
bool session_history_entry_matches_descriptor_ignoring_document_state_id(SessionHistoryEntry const& entry, SessionHistoryEntryDescriptor const& descriptor, MatchNestedHistories match_nested_histories)
|
|
{
|
|
auto entry_step = entry.step_value();
|
|
if (!entry_step.has_value() || *entry_step != descriptor.step)
|
|
return false;
|
|
if (entry.url() != descriptor.url)
|
|
return false;
|
|
if (descriptor.document_state.id != 0) {
|
|
auto document_state = entry.document_state();
|
|
if (!document_state || !session_history_document_state_descriptor_matches_document_state_ignoring_id(descriptor.document_state, *document_state, match_nested_histories))
|
|
return false;
|
|
}
|
|
if (entry.classic_history_api_state() != descriptor.classic_history_api_state)
|
|
return false;
|
|
if (entry.navigation_api_state() != descriptor.navigation_api_state || entry.navigation_api_key() != descriptor.navigation_api_key || entry.navigation_api_id() != descriptor.navigation_api_id)
|
|
return false;
|
|
if (entry.scroll_restoration_mode() != descriptor.scroll_restoration_mode)
|
|
return false;
|
|
if (descriptor.document_state.id != 0 && entry.scroll_position_data() != descriptor.scroll_position_data)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
}
|
|
|
|
template<>
|
|
ErrorOr<void> IPC::encode(Encoder& encoder, Web::HTML::SessionHistoryEntryDescriptor const& entry)
|
|
{
|
|
TRY(encoder.encode(entry.step));
|
|
TRY(encoder.encode(entry.url));
|
|
TRY(encoder.encode(entry.document_state));
|
|
TRY(encoder.encode(entry.classic_history_api_state));
|
|
TRY(encoder.encode(entry.navigation_api_state));
|
|
TRY(encoder.encode(entry.navigation_api_key));
|
|
TRY(encoder.encode(entry.navigation_api_id));
|
|
TRY(encoder.encode(entry.scroll_restoration_mode));
|
|
TRY(encoder.encode(entry.scroll_position_data));
|
|
return {};
|
|
}
|
|
|
|
template<>
|
|
ErrorOr<Web::HTML::SessionHistoryEntryDescriptor> IPC::decode(Decoder& decoder)
|
|
{
|
|
auto step = TRY(decoder.decode<i32>());
|
|
auto url = TRY(decoder.decode<URL::URL>());
|
|
auto document_state = TRY(decoder.decode<Web::HTML::SessionHistoryDocumentStateDescriptor>());
|
|
auto classic_history_api_state = TRY(decoder.decode<Web::HTML::SerializationRecord>());
|
|
auto navigation_api_state = TRY(decoder.decode<Web::HTML::SerializationRecord>());
|
|
auto navigation_api_key = TRY(decoder.decode<String>());
|
|
auto navigation_api_id = TRY(decoder.decode<String>());
|
|
auto scroll_restoration_mode = TRY(decoder.decode<Web::HTML::ScrollRestorationMode>());
|
|
auto scroll_position_data = TRY(decoder.decode<Web::HTML::SessionHistoryEntryScrollPositionData>());
|
|
|
|
return Web::HTML::SessionHistoryEntryDescriptor {
|
|
.step = step,
|
|
.url = move(url),
|
|
.document_state = move(document_state),
|
|
.classic_history_api_state = move(classic_history_api_state),
|
|
.navigation_api_state = move(navigation_api_state),
|
|
.navigation_api_key = move(navigation_api_key),
|
|
.navigation_api_id = move(navigation_api_id),
|
|
.scroll_restoration_mode = scroll_restoration_mode,
|
|
.scroll_position_data = move(scroll_position_data),
|
|
};
|
|
}
|
|
|
|
template<>
|
|
ErrorOr<void> IPC::encode(Encoder& encoder, Web::HTML::SessionHistoryEntryScrollPositionData const& scroll_position_data)
|
|
{
|
|
TRY(encoder.encode(scroll_position_data.viewport_scroll_position));
|
|
return {};
|
|
}
|
|
|
|
template<>
|
|
ErrorOr<Web::HTML::SessionHistoryEntryScrollPositionData> IPC::decode(Decoder& decoder)
|
|
{
|
|
auto viewport_scroll_position = TRY(decoder.decode<Optional<Web::CSSPixelPoint>>());
|
|
return Web::HTML::SessionHistoryEntryScrollPositionData {
|
|
.viewport_scroll_position = move(viewport_scroll_position),
|
|
};
|
|
}
|
|
|
|
template<>
|
|
ErrorOr<void> IPC::encode(Encoder& encoder, Web::HTML::SessionHistoryDocumentStateDescriptor const& document_state)
|
|
{
|
|
TRY(encoder.encode(document_state.id));
|
|
TRY(encoder.encode(document_state.history_policy_container));
|
|
TRY(encoder.encode(document_state.request_referrer));
|
|
TRY(encoder.encode(document_state.request_referrer_policy));
|
|
TRY(encoder.encode(document_state.initiator_origin));
|
|
TRY(encoder.encode(document_state.origin));
|
|
TRY(encoder.encode(document_state.about_base_url));
|
|
TRY(encoder.encode(document_state.resource));
|
|
TRY(encoder.encode(document_state.reload_pending));
|
|
TRY(encoder.encode(document_state.ever_populated));
|
|
TRY(encoder.encode(document_state.navigable_target_name));
|
|
TRY(encoder.encode(document_state.nested_histories));
|
|
return {};
|
|
}
|
|
|
|
template<>
|
|
ErrorOr<Web::HTML::SessionHistoryDocumentStateDescriptor> IPC::decode(Decoder& decoder)
|
|
{
|
|
auto id = TRY(decoder.decode<u64>());
|
|
auto history_policy_container = TRY(decoder.decode<Variant<Web::HTML::SerializedPolicyContainer, Web::HTML::DocumentState::Client>>());
|
|
auto request_referrer = TRY(decoder.decode<Web::Fetch::Infrastructure::Request::ReferrerType>());
|
|
auto request_referrer_policy = TRY(decoder.decode<Web::ReferrerPolicy::ReferrerPolicy>());
|
|
auto initiator_origin = TRY(decoder.decode<Optional<URL::Origin>>());
|
|
auto origin = TRY(decoder.decode<Optional<URL::Origin>>());
|
|
auto about_base_url = TRY(decoder.decode<Optional<URL::URL>>());
|
|
auto resource = TRY(decoder.decode<Variant<Empty, String, Web::HTML::POSTResource>>());
|
|
auto reload_pending = TRY(decoder.decode<bool>());
|
|
auto ever_populated = TRY(decoder.decode<bool>());
|
|
auto navigable_target_name = TRY(decoder.decode<String>());
|
|
auto nested_histories = TRY(decoder.decode<Vector<Web::HTML::SessionHistoryNestedHistoryDescriptor>>());
|
|
|
|
return Web::HTML::SessionHistoryDocumentStateDescriptor {
|
|
.id = id,
|
|
.history_policy_container = move(history_policy_container),
|
|
.request_referrer = move(request_referrer),
|
|
.request_referrer_policy = request_referrer_policy,
|
|
.initiator_origin = move(initiator_origin),
|
|
.origin = move(origin),
|
|
.about_base_url = move(about_base_url),
|
|
.resource = move(resource),
|
|
.reload_pending = reload_pending,
|
|
.ever_populated = ever_populated,
|
|
.navigable_target_name = move(navigable_target_name),
|
|
.nested_histories = move(nested_histories),
|
|
};
|
|
}
|
|
|
|
template<>
|
|
ErrorOr<void> IPC::encode(Encoder& encoder, Web::HTML::SessionHistoryNestedHistoryDescriptor const& nested_history)
|
|
{
|
|
TRY(encoder.encode(nested_history.id));
|
|
TRY(encoder.encode(nested_history.entries));
|
|
return {};
|
|
}
|
|
|
|
template<>
|
|
ErrorOr<Web::HTML::SessionHistoryNestedHistoryDescriptor> IPC::decode(Decoder& decoder)
|
|
{
|
|
auto id = TRY(decoder.decode<String>());
|
|
auto entries = TRY(decoder.decode<Vector<Web::HTML::SessionHistoryEntryDescriptor>>());
|
|
|
|
return Web::HTML::SessionHistoryNestedHistoryDescriptor { move(id), move(entries) };
|
|
}
|