LibWeb: Make PolicyContainer GC allocated

This is required to store Content Security Policies, as their
Directives are implemented as subclasses with overridden virtual
functions. Thus, they cannot be stored as generic Directive classes, as
it'll lose the ability to call overridden functions when they are
copied.
This commit is contained in:
Luke Wilde 2024-11-25 14:30:12 +00:00 committed by Sam Atkins
parent 2e27ffab6c
commit cae0ab2139
Notes: github-actions[bot] 2025-02-21 12:55:13 +00:00
43 changed files with 381 additions and 130 deletions

View file

@ -485,11 +485,13 @@ set(SOURCES
HTML/Scripting/SerializedEnvironmentSettingsObject.cpp
HTML/SelectedFile.cpp
HTML/SelectItem.cpp
HTML/SerializedPolicyContainer.cpp
HTML/SessionHistoryEntry.cpp
HTML/SessionHistoryTraversalQueue.cpp
HTML/ShadowRealmGlobalScope.cpp
HTML/SharedResourceRequest.cpp
HTML/SourceSet.cpp
HTML/SourceSnapshotParams.cpp
HTML/Storage.cpp
HTML/StorageEvent.cpp
HTML/StructuredSerialize.cpp

View file

@ -1,7 +1,7 @@
/*
* Copyright (c) 2018-2024, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2021-2023, Luke Wilde <lukew@serenityos.org>
* Copyright (c) 2021-2025, Luke Wilde <luke@ladybird.org>
* Copyright (c) 2021-2024, Sam Atkins <sam@ladybird.org>
* Copyright (c) 2024, Matthew Olsson <mattco@serenityos.org>
* Copyright (c) 2025, Jelle Raaijmakers <jelle@ladybird.org>
@ -118,6 +118,7 @@
#include <LibWeb/HTML/NavigationParams.h>
#include <LibWeb/HTML/Numbers.h>
#include <LibWeb/HTML/Parser/HTMLParser.h>
#include <LibWeb/HTML/PolicyContainers.h>
#include <LibWeb/HTML/PopStateEvent.h>
#include <LibWeb/HTML/Scripting/Agent.h>
#include <LibWeb/HTML/Scripting/ClassicScript.h>
@ -586,6 +587,7 @@ void Document::visit_edges(Cell::Visitor& visitor)
visitor.visit(m_local_storage_holder);
visitor.visit(m_session_storage_holder);
visitor.visit(m_render_blocking_elements);
visitor.visit(m_policy_container);
}
// https://w3c.github.io/selection-api/#dom-document-getselection
@ -3705,39 +3707,46 @@ void Document::set_active_sandboxing_flag_set(HTML::SandboxingFlagSet sandboxing
m_active_sandboxing_flag_set = sandboxing_flag_set;
}
HTML::PolicyContainer Document::policy_container() const
GC::Ref<HTML::PolicyContainer> Document::policy_container() const
{
return m_policy_container;
auto& realm = this->realm();
if (!m_policy_container) {
m_policy_container = realm.create<HTML::PolicyContainer>(realm);
}
return *m_policy_container;
}
void Document::set_policy_container(HTML::PolicyContainer policy_container)
void Document::set_policy_container(GC::Ref<HTML::PolicyContainer> policy_container)
{
m_policy_container = move(policy_container);
m_policy_container = policy_container;
}
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#snapshotting-source-snapshot-params
HTML::SourceSnapshotParams Document::snapshot_source_snapshot_params() const
GC::Ref<HTML::SourceSnapshotParams> Document::snapshot_source_snapshot_params() const
{
auto& realm = this->realm();
// To snapshot source snapshot params given a Document sourceDocument, return a new source snapshot params with
return realm.create<HTML::SourceSnapshotParams>(
// has transient activation
// true if sourceDocument's relevant global object has transient activation; otherwise false
as<HTML::Window>(HTML::relevant_global_object(*this)).has_transient_activation(),
// has transient activation
// true if sourceDocument's relevant global object has transient activation; otherwise false
// sandboxing flags
// sourceDocument's active sandboxing flag set
// allows downloading
// false if sourceDocument's active sandboxing flag set has the sandboxed downloads browsing context flag set; otherwise true
// fetch client
// sourceDocument's relevant settings object
// source policy container
// a clone of sourceDocument's policy container
// sandboxing flags
// sourceDocument's active sandboxing flag set
m_active_sandboxing_flag_set,
return HTML::SourceSnapshotParams {
.has_transient_activation = as<HTML::Window>(HTML::relevant_global_object(*this)).has_transient_activation(),
.sandboxing_flags = m_active_sandboxing_flag_set,
.allows_downloading = !has_flag(m_active_sandboxing_flag_set, HTML::SandboxingFlagSet::SandboxedDownloads),
.fetch_client = relevant_settings_object(),
.source_policy_container = m_policy_container
};
// allows downloading
// false if sourceDocument's active sandboxing flag set has the sandboxed downloads browsing context flag set; otherwise true
!has_flag(m_active_sandboxing_flag_set, HTML::SandboxingFlagSet::SandboxedDownloads),
// fetch client
// sourceDocument's relevant settings object
relevant_settings_object(),
// source policy container
// a clone of sourceDocument's policy container
policy_container()->clone(realm));
}
// https://html.spec.whatwg.org/multipage/document-sequences.html#descendant-navigables

View file

@ -548,8 +548,8 @@ public:
void set_active_sandboxing_flag_set(HTML::SandboxingFlagSet);
// https://html.spec.whatwg.org/multipage/dom.html#concept-document-policy-container
HTML::PolicyContainer policy_container() const;
void set_policy_container(HTML::PolicyContainer);
GC::Ref<HTML::PolicyContainer> policy_container() const;
void set_policy_container(GC::Ref<HTML::PolicyContainer>);
Vector<GC::Root<HTML::Navigable>> descendant_navigables();
Vector<GC::Root<HTML::Navigable>> const descendant_navigables() const;
@ -635,7 +635,7 @@ public:
u32 unload_counter() const { return m_unload_counter; }
HTML::SourceSnapshotParams snapshot_source_snapshot_params() const;
GC::Ref<HTML::SourceSnapshotParams> snapshot_source_snapshot_params() const;
void update_for_history_step_application(GC::Ref<HTML::SessionHistoryEntry>, bool do_not_reactivate, size_t script_history_length, size_t script_history_index, Optional<Bindings::NavigationType> navigation_type, Optional<Vector<GC::Ref<HTML::SessionHistoryEntry>>> entries_for_navigation_api = {}, GC::Ptr<HTML::SessionHistoryEntry> previous_entry_for_activation = {}, bool update_navigation_api = true);
@ -1018,7 +1018,7 @@ private:
HTML::SandboxingFlagSet m_active_sandboxing_flag_set;
// https://html.spec.whatwg.org/multipage/dom.html#concept-document-policy-container
HTML::PolicyContainer m_policy_container;
mutable GC::Ptr<HTML::PolicyContainer> m_policy_container;
// https://html.spec.whatwg.org/multipage/interaction.html#visibility-state
HTML::VisibilityState m_visibility_state { HTML::VisibilityState::Hidden };

View file

@ -21,6 +21,8 @@ template<typename MutateDocument>
GC::Ref<DOM::Document> create_document_for_inline_content(GC::Ptr<HTML::Navigable> navigable, Optional<String> navigation_id, HTML::UserNavigationInvolvement user_involvement, MutateDocument mutate_document)
{
auto& vm = navigable->vm();
VERIFY(navigable->active_document());
auto& realm = navigable->active_document()->realm();
// 1. Let origin be a new opaque origin.
URL::Origin origin {};
@ -66,7 +68,7 @@ GC::Ref<DOM::Document> create_document_for_inline_content(GC::Ptr<HTML::Navigabl
navigation_params->coop_enforcement_result = move(coop_enforcement_result);
navigation_params->reserved_environment = {};
navigation_params->origin = move(origin);
navigation_params->policy_container = HTML::PolicyContainer {};
navigation_params->policy_container = vm.heap().allocate<HTML::PolicyContainer>(realm);
navigation_params->final_sandboxing_flag_set = HTML::SandboxingFlagSet {};
navigation_params->opener_policy = move(coop);
navigation_params->about_base_url = {};

View file

@ -181,10 +181,10 @@ WebIDL::ExceptionOr<GC::Ref<Infrastructure::FetchController>> fetch(JS::Realm& r
// 1. If requests client is non-null, then set requests policy container to a clone of requests clients
// policy container.
if (request.client() != nullptr)
request.set_policy_container(request.client()->policy_container());
request.set_policy_container(request.client()->policy_container()->clone(realm));
// 2. Otherwise, set requests policy container to a new policy container.
else
request.set_policy_container(HTML::PolicyContainer {});
request.set_policy_container(realm.create<HTML::PolicyContainer>(realm));
}
// 13. If requests header list does not contain `Accept`, then:
@ -301,8 +301,8 @@ WebIDL::ExceptionOr<GC::Ptr<PendingResponse>> main_fetch(JS::Realm& realm, Infra
// 8. If requests referrer policy is the empty string, then set requests referrer policy to requests policy
// containers referrer policy.
if (request->referrer_policy() == ReferrerPolicy::ReferrerPolicy::EmptyString) {
VERIFY(request->policy_container().has<HTML::PolicyContainer>());
request->set_referrer_policy(request->policy_container().get<HTML::PolicyContainer>().referrer_policy);
VERIFY(request->policy_container().has<GC::Ref<HTML::PolicyContainer>>());
request->set_referrer_policy(request->policy_container().get<GC::Ref<HTML::PolicyContainer>>()->referrer_policy);
}
// 9. If requests referrer is not "no-referrer", then set requests referrer to the result of invoking determine

View file

@ -33,6 +33,9 @@ void Request::visit_edges(JS::Cell::Visitor& visitor)
[&](GC::Ptr<HTML::EnvironmentSettingsObject> const& value) { visitor.visit(value); },
[](auto const&) {});
visitor.visit(m_pending_responses);
m_policy_container.visit(
[&](GC::Ref<HTML::PolicyContainer> const& policy_container) { visitor.visit(policy_container); },
[](auto const&) {});
}
GC::Ref<Request> Request::create(JS::VM& vm)
@ -359,7 +362,7 @@ bool Request::cross_origin_embedder_policy_allows_credentials() const
return true;
// 3. If requests clients policy containers embedder policys value is not "credentialless", then return true.
if (m_policy_container.has<HTML::PolicyContainer>() && m_policy_container.get<HTML::PolicyContainer>().embedder_policy.value != HTML::EmbedderPolicyValue::Credentialless)
if (m_policy_container.has<GC::Ref<HTML::PolicyContainer>>() && m_policy_container.get<GC::Ref<HTML::PolicyContainer>>()->embedder_policy.value != HTML::EmbedderPolicyValue::Credentialless)
return true;
// 4. If requests origin is same origin with requests current URLs origin and request does not have a redirect-tainted origin, then return true.

View file

@ -171,7 +171,7 @@ public:
using BodyType = Variant<Empty, ByteBuffer, GC::Ref<Body>>;
using OriginType = Variant<Origin, URL::Origin>;
using PolicyContainerType = Variant<PolicyContainer, HTML::PolicyContainer>;
using PolicyContainerType = Variant<PolicyContainer, GC::Ref<HTML::PolicyContainer>>;
using ReferrerType = Variant<Referrer, URL::URL>;
using ReservedClientType = GC::Ptr<HTML::Environment>;
using WindowType = Variant<Window, GC::Ptr<HTML::EnvironmentSettingsObject>>;

View file

@ -583,6 +583,7 @@ struct POSTResource;
struct ScrollOptions;
struct ScrollToOptions;
struct SerializedFormData;
struct SerializedPolicyContainer;
struct StructuredSerializeOptions;
struct SyntheticRealmSettings;
struct ToggleTaskTracker;

View file

@ -251,7 +251,7 @@ WebIDL::ExceptionOr<BrowsingContext::BrowsingContextAndDocument> BrowsingContext
document->set_referrer(creator->url().serialize());
// 2. Set document's policy container to a clone of creator's policy container.
document->set_policy_container(creator->policy_container());
document->set_policy_container(creator->policy_container()->clone(document->realm()));
// 3. If creator's origin is same origin with creator's relevant settings object's top-level origin,
if (creator->origin().is_same_origin(creator->relevant_settings_object().top_level_origin)) {

View file

@ -38,6 +38,9 @@ void DocumentState::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_document);
m_history_policy_container.visit(
[&](GC::Ref<PolicyContainer> const& policy_container) { visitor.visit(policy_container); },
[](auto const&) {});
for (auto& nested_history : m_nested_histories) {
visitor.visit(nested_history.entries);
}

View file

@ -40,8 +40,8 @@ public:
[[nodiscard]] GC::Ptr<DOM::Document> document() const { return m_document; }
void set_document(GC::Ptr<DOM::Document> document) { m_document = document; }
[[nodiscard]] Variant<PolicyContainer, Client> history_policy_container() const { return m_history_policy_container; }
void set_history_policy_container(Variant<PolicyContainer, Client> history_policy_container) { m_history_policy_container = move(history_policy_container); }
[[nodiscard]] Variant<GC::Ref<PolicyContainer>, Client> history_policy_container() const { return m_history_policy_container; }
void set_history_policy_container(Variant<GC::Ref<PolicyContainer>, Client> history_policy_container) { m_history_policy_container = move(history_policy_container); }
[[nodiscard]] Fetch::Infrastructure::Request::ReferrerType request_referrer() const { return m_request_referrer; }
void set_request_referrer(Fetch::Infrastructure::Request::ReferrerType request_referrer) { m_request_referrer = move(request_referrer); }
@ -82,7 +82,7 @@ private:
GC::Ptr<DOM::Document> m_document;
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#document-state-history-policy-container
Variant<PolicyContainer, Client> m_history_policy_container { Client::Tag };
Variant<GC::Ref<PolicyContainer>, Client> m_history_policy_container { Client::Tag };
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#document-state-request-referrer
Fetch::Infrastructure::Request::ReferrerType m_request_referrer { Fetch::Infrastructure::Request::Referrer::Client };

View file

@ -1,9 +1,12 @@
/*
* Copyright (c) 2024, Jamie Mansfield <jmansfield@cadixdev.org>
* Copyright (c) 2025, Luke Wilde <luke@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibIPC/Decoder.h>
#include <LibIPC/Encoder.h>
#include <LibWeb/HTML/EmbedderPolicy.h>
namespace Web::HTML {
@ -33,3 +36,31 @@ Optional<EmbedderPolicyValue> embedder_policy_value_from_string(StringView strin
}
}
namespace IPC {
template<>
ErrorOr<void> encode(Encoder& encoder, Web::HTML::EmbedderPolicy const& embedder_policy)
{
TRY(encoder.encode(embedder_policy.value));
TRY(encoder.encode(embedder_policy.reporting_endpoint));
TRY(encoder.encode(embedder_policy.report_only_value));
TRY(encoder.encode(embedder_policy.report_only_reporting_endpoint));
return {};
}
template<>
ErrorOr<Web::HTML::EmbedderPolicy> decode(Decoder& decoder)
{
Web::HTML::EmbedderPolicy embedder_policy {};
embedder_policy.value = TRY(decoder.decode<Web::HTML::EmbedderPolicyValue>());
embedder_policy.reporting_endpoint = TRY(decoder.decode<String>());
embedder_policy.report_only_value = TRY(decoder.decode<Web::HTML::EmbedderPolicyValue>());
embedder_policy.report_only_reporting_endpoint = TRY(decoder.decode<String>());
return embedder_policy;
}
}

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2024, Jamie Mansfield <jmansfield@cadixdev.org>
* Copyright (c) 2025, Luke Wilde <luke@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -9,6 +10,7 @@
#include <AK/Optional.h>
#include <AK/String.h>
#include <AK/StringView.h>
#include <LibIPC/Forward.h>
namespace Web::HTML {
@ -42,3 +44,13 @@ struct EmbedderPolicy {
};
}
namespace IPC {
template<>
ErrorOr<void> encode(Encoder&, Web::HTML::EmbedderPolicy const&);
template<>
ErrorOr<Web::HTML::EmbedderPolicy> decode(Decoder&);
}

View file

@ -286,7 +286,7 @@ GC::Ptr<Fetch::Infrastructure::Request> HTMLLinkElement::create_link_request(HTM
auto request = create_potential_CORS_request(vm(), *url, options.destination, options.crossorigin);
// 6. Set request's policy container to options's policy container.
request->set_policy_container(options.policy_container);
request->set_policy_container(GC::Ref { *options.policy_container });
// 7. Set request's integrity metadata to options's integrity.
request->set_integrity_metadata(options.integrity);

View file

@ -92,7 +92,7 @@ private:
GC::Ptr<HTML::EnvironmentSettingsObject> environment;
// policy container
// A policy container
HTML::PolicyContainer policy_container;
GC::Ptr<HTML::PolicyContainer> policy_container;
// document (default null)
// Null or a Document
GC::Ptr<Web::DOM::Document> document;

View file

@ -1,6 +1,7 @@
/*
* Copyright (c) 2022-2024, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2023, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
* Copyright (c) 2025, Luke Wilde <luke@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -608,42 +609,41 @@ Vector<GC::Ref<SessionHistoryEntry>>& Navigable::get_session_history_entries() c
}
// https://html.spec.whatwg.org/multipage/browsers.html#determining-navigation-params-policy-container
static PolicyContainer determine_navigation_params_policy_container(URL::URL const& response_url,
Optional<PolicyContainer> history_policy_container,
Optional<PolicyContainer> initiator_policy_container,
Optional<PolicyContainer> parent_policy_container,
Optional<PolicyContainer> response_policy_container)
static GC::Ref<PolicyContainer> determine_navigation_params_policy_container(URL::URL const& response_url,
JS::Realm& realm,
GC::Ptr<PolicyContainer> history_policy_container,
GC::Ptr<PolicyContainer> initiator_policy_container,
GC::Ptr<PolicyContainer> parent_policy_container,
GC::Ptr<PolicyContainer> response_policy_container)
{
// NOTE: The clone a policy container AO is just a C++ copy
// 1. If historyPolicyContainer is not null, then:
if (history_policy_container.has_value()) {
if (history_policy_container) {
// FIXME: 1. Assert: responseURL requires storing the policy container in history.
// 2. Return a clone of historyPolicyContainer.
return *history_policy_container;
return history_policy_container->clone(realm);
}
// 2. If responseURL is about:srcdoc, then:
if (response_url == URL::about_srcdoc()) {
// 1. Assert: parentPolicyContainer is not null.
VERIFY(parent_policy_container.has_value());
VERIFY(parent_policy_container);
// 2. Return a clone of parentPolicyContainer.
return *parent_policy_container;
return parent_policy_container->clone(realm);
}
// 3. If responseURL is local and initiatorPolicyContainer is not null, then return a clone of initiatorPolicyContainer.
if (Fetch::Infrastructure::is_local_url(response_url) && initiator_policy_container.has_value())
return *initiator_policy_container;
if (Fetch::Infrastructure::is_local_url(response_url) && initiator_policy_container)
return initiator_policy_container->clone(realm);
// 4. If responsePolicyContainer is not null, then return responsePolicyContainer.
// FIXME: File a spec issue to say "a clone of" here for consistency
if (response_policy_container.has_value())
return *response_policy_container;
if (response_policy_container)
return response_policy_container->clone(realm);
// 5. Return a new policy container.
return {};
return realm.create<PolicyContainer>(realm);
}
// https://html.spec.whatwg.org/multipage/browsers.html#obtain-coop
@ -724,15 +724,17 @@ static GC::Ref<NavigationParams> create_navigation_params_from_a_srcdoc_resource
// 6. Let policyContainer be the result of determining navigation params policy container given response's URL,
// entry's document state's history policy container, null, navigable's container document's policy container, and null.
Optional<PolicyContainer> history_policy_container = entry->document_state()->history_policy_container().visit(
[](PolicyContainer const& c) -> Optional<PolicyContainer> { return c; },
[](DocumentState::Client) -> Optional<PolicyContainer> { return {}; });
PolicyContainer policy_container;
GC::Ptr<PolicyContainer> history_policy_container = entry->document_state()->history_policy_container().visit(
[](GC::Ref<PolicyContainer> const& c) -> GC::Ptr<PolicyContainer> { return c; },
[](DocumentState::Client) -> GC::Ptr<PolicyContainer> { return {}; });
GC::Ptr<PolicyContainer> policy_container;
if (navigable->container()) {
// NOTE: Specification assumes that only navigables corresponding to iframes can be navigated to about:srcdoc.
// We also use srcdoc to implement load_html() for top level navigables so we need to null check container
// because it might be null.
policy_container = determine_navigation_params_policy_container(*response->url(), history_policy_container, {}, navigable->container_document()->policy_container(), {});
policy_container = determine_navigation_params_policy_container(*response->url(), realm, history_policy_container, {}, navigable->container_document()->policy_container(), {});
} else {
policy_container = realm.create<PolicyContainer>(realm);
}
// 7. Return a new navigation params, with
@ -757,7 +759,7 @@ static GC::Ref<NavigationParams> create_navigation_params_from_a_srcdoc_resource
navigation_params->response = response;
navigation_params->coop_enforcement_result = move(coop_enforcement_result);
navigation_params->origin = move(response_origin);
navigation_params->policy_container = policy_container;
navigation_params->policy_container = *policy_container;
navigation_params->final_sandboxing_flag_set = target_snapshot_params.sandboxing_flags;
navigation_params->opener_policy = move(coop);
navigation_params->about_base_url = entry->document_state()->about_base_url();
@ -904,7 +906,7 @@ static WebIDL::ExceptionOr<Navigable::NavigationParamsVariant> create_navigation
SandboxingFlagSet final_sandbox_flags = {};
// 14. Let responsePolicyContainer be null.
Optional<PolicyContainer> response_policy_container = {};
GC::Ptr<PolicyContainer> response_policy_container = {};
// 15. Let responseCOOP be a new opener policy.
OpenerPolicy response_coop = {};
@ -1096,10 +1098,10 @@ static WebIDL::ExceptionOr<Navigable::NavigationParamsVariant> create_navigation
// 23. Let resultPolicyContainer be the result of determining navigation params policy container given response's URL,
// entry's document state's history policy container, sourceSnapshotParams's source policy container, null, and responsePolicyContainer.
Optional<PolicyContainer> history_policy_container = entry->document_state()->history_policy_container().visit(
[](PolicyContainer const& c) -> Optional<PolicyContainer> { return c; },
[](DocumentState::Client) -> Optional<PolicyContainer> { return {}; });
auto result_policy_container = determine_navigation_params_policy_container(*response_holder->response()->url(), history_policy_container, source_snapshot_params.source_policy_container, {}, response_policy_container);
GC::Ptr<PolicyContainer> history_policy_container = entry->document_state()->history_policy_container().visit(
[](GC::Ref<PolicyContainer> const& c) -> GC::Ptr<PolicyContainer> { return c; },
[](DocumentState::Client) -> GC::Ptr<PolicyContainer> { return {}; });
auto result_policy_container = determine_navigation_params_policy_container(*response_holder->response()->url(), realm, history_policy_container, source_snapshot_params.source_policy_container, {}, response_policy_container);
// 24. If navigable's container is an iframe, and response's timing allow passed flag is set, then set container's pending resource-timing start time to null.
if (navigable->container() && is<HTML::HTMLIFrameElement>(*navigable->container()) && response_holder->response()->timing_allow_passed())
@ -1298,7 +1300,14 @@ WebIDL::ExceptionOr<void> Navigable::populate_session_history_entry_document(
// 2. Set entry's document state's origin to document's origin.
entry->document_state()->set_origin(document->origin());
// FIXME: 3. If document's URL requires storing the policy container in history, then:
// 3. If document's URL requires storing the policy container in history, then:
if (url_requires_storing_the_policy_container_in_history(document->url())) {
// 1. Assert: navigationParams is a navigation params (i.e., neither null nor a non-fetch scheme navigation params).
VERIFY(navigation_params.has<GC::Ref<NavigationParams>>());
// 2. Set entry's document state's history policy container to navigationParams's policy container.
entry->document_state()->set_history_policy_container(GC::Ref { *navigation_params.get<GC::Ref<NavigationParams>>()->policy_container });
}
}
// 3. If entry's document state's request referrer is "client", and navigationParams is a navigation params (i.e., neither null nor a non-fetch scheme navigation params), then:
@ -1810,7 +1819,7 @@ GC::Ptr<DOM::Document> Navigable::evaluate_javascript_url(URL::URL const& url, U
}
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#navigate-to-a-javascript:-url
void Navigable::navigate_to_a_javascript_url(URL::URL const& url, HistoryHandlingBehavior history_handling, SourceSnapshotParams, URL::Origin const& initiator_origin, UserNavigationInvolvement user_involvement, CSPNavigationType csp_navigation_type, String navigation_id)
void Navigable::navigate_to_a_javascript_url(URL::URL const& url, HistoryHandlingBehavior history_handling, GC::Ref<SourceSnapshotParams>, URL::Origin const& initiator_origin, UserNavigationInvolvement user_involvement, CSPNavigationType csp_navigation_type, String navigation_id)
{
// 1. Assert: historyHandling is "replace".
VERIFY(history_handling == HistoryHandlingBehavior::Replace);

View file

@ -154,7 +154,7 @@ public:
WebIDL::ExceptionOr<void> navigate_to_a_fragment(URL::URL const&, HistoryHandlingBehavior, UserNavigationInvolvement, GC::Ptr<DOM::Element> source_element, Optional<SerializationRecord> navigation_api_state, String navigation_id);
GC::Ptr<DOM::Document> evaluate_javascript_url(URL::URL const&, URL::Origin const& new_document_origin, UserNavigationInvolvement, String navigation_id);
void navigate_to_a_javascript_url(URL::URL const&, HistoryHandlingBehavior, SourceSnapshotParams, URL::Origin const& initiator_origin, UserNavigationInvolvement, CSPNavigationType csp_navigation_type, String navigation_id);
void navigate_to_a_javascript_url(URL::URL const&, HistoryHandlingBehavior, GC::Ref<SourceSnapshotParams>, URL::Origin const& initiator_origin, UserNavigationInvolvement, CSPNavigationType csp_navigation_type, String navigation_id);
bool allowed_by_sandboxing_to_navigate(Navigable const& target, SourceSnapshotParams const&);

View file

@ -23,6 +23,7 @@ void NavigationParams::visit_edges(Visitor& visitor)
visitor.visit(response);
visitor.visit(fetch_controller);
visitor.visit(reserved_environment);
visitor.visit(policy_container);
}
void NonFetchSchemeNavigationParams::visit_edges(Visitor& visitor)

View file

@ -60,7 +60,7 @@ struct NavigationParams : JS::Cell {
URL::Origin origin;
// a policy container to use for the new Document
PolicyContainer policy_container;
GC::Ptr<PolicyContainer> policy_container;
// a sandboxing flag set to impose on the new Document
SandboxingFlagSet final_sandboxing_flag_set = {};

View file

@ -1,29 +1,69 @@
/*
* Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org>
* Copyright (c) 2025, Luke Wilde <luke@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibIPC/Decoder.h>
#include <LibIPC/Encoder.h>
#include <LibJS/Runtime/Realm.h>
#include <LibURL/URL.h>
#include <LibWeb/Fetch/Infrastructure/URL.h>
#include <LibWeb/HTML/PolicyContainers.h>
#include <LibWeb/HTML/SerializedPolicyContainer.h>
namespace IPC {
namespace Web::HTML {
template<>
ErrorOr<void> encode(IPC::Encoder& encoder, Web::HTML::PolicyContainer const& policy_container)
GC_DEFINE_ALLOCATOR(PolicyContainer);
PolicyContainer::PolicyContainer(JS::Realm&)
{
TRY(encode(encoder, policy_container.referrer_policy));
return {};
}
template<>
ErrorOr<Web::HTML::PolicyContainer> decode(IPC::Decoder& decoder)
// https://html.spec.whatwg.org/multipage/browsers.html#requires-storing-the-policy-container-in-history
bool url_requires_storing_the_policy_container_in_history(URL::URL const& url)
{
auto referrer_policy = TRY(decoder.decode<Web::ReferrerPolicy::ReferrerPolicy>());
// 1. If url's scheme is "blob", then return false.
if (url.scheme() == "blob"sv)
return false;
return Web::HTML::PolicyContainer { .referrer_policy = referrer_policy };
// 2. If url is local, then return true.
// 3. Return false.
return Fetch::Infrastructure::is_local_url(url);
}
GC::Ref<PolicyContainer> create_a_policy_container_from_serialized_policy_container(JS::Realm& realm, SerializedPolicyContainer const& serialized_policy_container)
{
GC::Ref<PolicyContainer> result = realm.create<PolicyContainer>(realm);
result->embedder_policy = serialized_policy_container.embedder_policy;
result->referrer_policy = serialized_policy_container.referrer_policy;
return result;
}
// https://html.spec.whatwg.org/multipage/browsers.html#clone-a-policy-container
GC::Ref<PolicyContainer> PolicyContainer::clone(JS::Realm& realm) const
{
// 1. Let clone be a new policy container.
auto clone = realm.create<PolicyContainer>(realm);
// FIXME: 2. For each policy in policyContainer's CSP list, append a copy of policy into clone's CSP list.
// 3. Set clone's embedder policy to a copy of policyContainer's embedder policy.
// NOTE: This is a C++ copy.
clone->embedder_policy = embedder_policy;
// 4. Set clone's referrer policy to policyContainer's referrer policy.
clone->referrer_policy = referrer_policy;
// 5. Return clone.
return clone;
}
SerializedPolicyContainer PolicyContainer::serialize() const
{
return SerializedPolicyContainer {
.embedder_policy = embedder_policy,
.referrer_policy = referrer_policy,
};
}
}

View file

@ -1,12 +1,16 @@
/*
* Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2025, Luke Wilde <luke@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibIPC/Forward.h>
#include <LibGC/CellAllocator.h>
#include <LibJS/Heap/Cell.h>
#include <LibURL/Forward.h>
#include <LibWeb/Forward.h>
#include <LibWeb/HTML/EmbedderPolicy.h>
#include <LibWeb/ReferrerPolicy/ReferrerPolicy.h>
@ -14,7 +18,13 @@ namespace Web::HTML {
// https://html.spec.whatwg.org/multipage/origin.html#policy-container
// A policy container is a struct containing policies that apply to a Document, a WorkerGlobalScope, or a WorkletGlobalScope. It has the following items:
struct PolicyContainer {
struct PolicyContainer : public JS::Cell {
GC_CELL(PolicyContainer, JS::Cell)
GC_DECLARE_ALLOCATOR(PolicyContainer);
public:
virtual ~PolicyContainer() = default;
// https://html.spec.whatwg.org/multipage/origin.html#policy-container-csp-list
// FIXME: A CSP list, which is a CSP list. It is initially empty.
@ -25,14 +35,17 @@ struct PolicyContainer {
// https://html.spec.whatwg.org/multipage/origin.html#policy-container-referrer-policy
// A referrer policy, which is a referrer policy. It is initially the default referrer policy.
ReferrerPolicy::ReferrerPolicy referrer_policy { ReferrerPolicy::DEFAULT_REFERRER_POLICY };
[[nodiscard]] GC::Ref<PolicyContainer> clone(JS::Realm&) const;
[[nodiscard]] SerializedPolicyContainer serialize() const;
private:
PolicyContainer(JS::Realm&);
};
}
// https://html.spec.whatwg.org/multipage/browsers.html#requires-storing-the-policy-container-in-history
[[nodiscard]] bool url_requires_storing_the_policy_container_in_history(URL::URL const& url);
namespace IPC {
template<>
ErrorOr<void> encode(IPC::Encoder&, Web::HTML::PolicyContainer const&);
[[nodiscard]] GC::Ref<PolicyContainer> create_a_policy_container_from_serialized_policy_container(JS::Realm&, SerializedPolicyContainer const&);
template<>
ErrorOr<Web::HTML::PolicyContainer> decode(IPC::Decoder&);
}

View file

@ -10,12 +10,12 @@ namespace Web::HTML {
GC_DEFINE_ALLOCATOR(EnvironmentSettingsSnapshot);
EnvironmentSettingsSnapshot::EnvironmentSettingsSnapshot(NonnullOwnPtr<JS::ExecutionContext> execution_context, SerializedEnvironmentSettingsObject const& serialized_settings)
EnvironmentSettingsSnapshot::EnvironmentSettingsSnapshot(JS::Realm& realm, NonnullOwnPtr<JS::ExecutionContext> execution_context, SerializedEnvironmentSettingsObject const& serialized_settings)
: EnvironmentSettingsObject(move(execution_context))
, m_api_url_character_encoding(serialized_settings.api_url_character_encoding)
, m_url(serialized_settings.api_base_url)
, m_origin(serialized_settings.origin)
, m_policy_container(serialized_settings.policy_container)
, m_policy_container(create_a_policy_container_from_serialized_policy_container(realm, serialized_settings.policy_container))
, m_time_origin(serialized_settings.time_origin)
{
// Why can't we put these in the init list? grandparent class members are strange it seems
@ -27,4 +27,10 @@ EnvironmentSettingsSnapshot::EnvironmentSettingsSnapshot(NonnullOwnPtr<JS::Execu
// Out of line to ensure this class has a key function
EnvironmentSettingsSnapshot::~EnvironmentSettingsSnapshot() = default;
void EnvironmentSettingsSnapshot::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_policy_container);
}
}

View file

@ -18,7 +18,7 @@ class EnvironmentSettingsSnapshot final
GC_DECLARE_ALLOCATOR(EnvironmentSettingsSnapshot);
public:
EnvironmentSettingsSnapshot(NonnullOwnPtr<JS::ExecutionContext>, SerializedEnvironmentSettingsObject const&);
EnvironmentSettingsSnapshot(JS::Realm&, NonnullOwnPtr<JS::ExecutionContext>, SerializedEnvironmentSettingsObject const&);
virtual ~EnvironmentSettingsSnapshot() override;
@ -26,15 +26,18 @@ public:
String api_url_character_encoding() const override { return m_api_url_character_encoding; }
URL::URL api_base_url() const override { return m_url; }
URL::Origin origin() const override { return m_origin; }
PolicyContainer policy_container() const override { return m_policy_container; }
GC::Ref<PolicyContainer> policy_container() const override { return m_policy_container; }
CanUseCrossOriginIsolatedAPIs cross_origin_isolated_capability() const override { return CanUseCrossOriginIsolatedAPIs::No; }
double time_origin() const override { return m_time_origin; }
protected:
virtual void visit_edges(Cell::Visitor&) override;
private:
String m_api_url_character_encoding;
URL::URL m_url;
URL::Origin m_origin;
HTML::PolicyContainer m_policy_container;
GC::Ref<PolicyContainer> m_policy_container;
double m_time_origin { 0 };
};

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
* Copyright (c) 2021-2025, Luke Wilde <luke@ladybird.org>
* Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2022, networkException <networkexception@serenityos.org>
* Copyright (c) 2024, Shannon Booth <shannon@serenityos.org>
@ -565,7 +565,7 @@ SerializedEnvironmentSettingsObject EnvironmentSettingsObject::serialize()
object.api_url_character_encoding = api_url_character_encoding();
object.api_base_url = api_base_url();
object.origin = origin();
object.policy_container = policy_container();
object.policy_container = policy_container()->serialize();
object.cross_origin_isolated_capability = cross_origin_isolated_capability();
return object;

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
* Copyright (c) 2021-2025, Luke Wilde <luke@ladybird.org>
* Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2024, Shannon Booth <shannon@serenityos.org>
*
@ -83,7 +83,7 @@ public:
virtual URL::Origin origin() const = 0;
// A policy container https://html.spec.whatwg.org/multipage/webappapis.html#concept-settings-object-policy-container
virtual PolicyContainer policy_container() const = 0;
virtual GC::Ref<PolicyContainer> policy_container() const = 0;
// https://html.spec.whatwg.org/multipage/webappapis.html#concept-settings-object-cross-origin-isolated-capability
virtual CanUseCrossOriginIsolatedAPIs cross_origin_isolated_capability() const = 0;

View file

@ -39,7 +39,7 @@ ErrorOr<Web::HTML::SerializedEnvironmentSettingsObject> decode(Decoder& decoder)
object.api_url_character_encoding = TRY(decoder.decode<String>());
object.api_base_url = TRY(decoder.decode<URL::URL>());
object.origin = TRY(decoder.decode<URL::Origin>());
object.policy_container = TRY(decoder.decode<Web::HTML::PolicyContainer>());
object.policy_container = TRY(decoder.decode<Web::HTML::SerializedPolicyContainer>());
object.cross_origin_isolated_capability = TRY(decoder.decode<Web::HTML::CanUseCrossOriginIsolatedAPIs>());
object.time_origin = TRY(decoder.decode<double>());

View file

@ -10,7 +10,7 @@
#include <LibIPC/Forward.h>
#include <LibURL/Origin.h>
#include <LibURL/URL.h>
#include <LibWeb/HTML/PolicyContainers.h>
#include <LibWeb/HTML/SerializedPolicyContainer.h>
namespace Web::HTML {
@ -28,7 +28,7 @@ struct SerializedEnvironmentSettingsObject {
String api_url_character_encoding;
URL::URL api_base_url;
URL::Origin origin;
PolicyContainer policy_container;
SerializedPolicyContainer policy_container;
CanUseCrossOriginIsolatedAPIs cross_origin_isolated_capability;
double time_origin;
};

View file

@ -111,7 +111,7 @@ URL::Origin WindowEnvironmentSettingsObject::origin() const
}
// https://html.spec.whatwg.org/multipage/window-object.html#script-settings-for-window-objects:concept-settings-object-policy-container
PolicyContainer WindowEnvironmentSettingsObject::policy_container() const
GC::Ref<PolicyContainer> WindowEnvironmentSettingsObject::policy_container() const
{
// Return the policy container of window's associated Document.
return m_window->associated_document().policy_container();

View file

@ -24,7 +24,7 @@ public:
virtual String api_url_character_encoding() const override;
virtual URL::URL api_base_url() const override;
virtual URL::Origin origin() const override;
virtual PolicyContainer policy_container() const override;
virtual GC::Ref<PolicyContainer> policy_container() const override;
virtual CanUseCrossOriginIsolatedAPIs cross_origin_isolated_capability() const override;
virtual double time_origin() const override;

View file

@ -66,7 +66,7 @@ URL::Origin WorkerEnvironmentSettingsObject::origin() const
return m_origin;
}
PolicyContainer WorkerEnvironmentSettingsObject::policy_container() const
GC::Ref<PolicyContainer> WorkerEnvironmentSettingsObject::policy_container() const
{
// Return worker global scope's policy container.
return m_global_scope->policy_container();

View file

@ -33,7 +33,7 @@ public:
String api_url_character_encoding() const override { return m_api_url_character_encoding; }
URL::URL api_base_url() const override;
URL::Origin origin() const override;
PolicyContainer policy_container() const override;
GC::Ref<PolicyContainer> policy_container() const override;
CanUseCrossOriginIsolatedAPIs cross_origin_isolated_capability() const override;
double time_origin() const override;

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2025, Luke Wilde <luke@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibIPC/Decoder.h>
#include <LibIPC/Encoder.h>
#include <LibWeb/HTML/SerializedPolicyContainer.h>
namespace IPC {
template<>
ErrorOr<void> encode(Encoder& encoder, Web::HTML::SerializedPolicyContainer const& serialized_policy_container)
{
TRY(encoder.encode(serialized_policy_container.embedder_policy));
TRY(encoder.encode(serialized_policy_container.referrer_policy));
return {};
}
template<>
ErrorOr<Web::HTML::SerializedPolicyContainer> decode(Decoder& decoder)
{
Web::HTML::SerializedPolicyContainer serialized_policy_container {};
serialized_policy_container.embedder_policy = TRY(decoder.decode<Web::HTML::EmbedderPolicy>());
serialized_policy_container.referrer_policy = TRY(decoder.decode<Web::ReferrerPolicy::ReferrerPolicy>());
return serialized_policy_container;
}
}

View file

@ -0,0 +1,29 @@
/*
* Copyright (c) 2025, Luke Wilde <luke@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibWeb/HTML/EmbedderPolicy.h>
#include <LibWeb/ReferrerPolicy/ReferrerPolicy.h>
namespace Web::HTML {
struct SerializedPolicyContainer {
EmbedderPolicy embedder_policy;
ReferrerPolicy::ReferrerPolicy referrer_policy;
};
}
namespace IPC {
template<>
ErrorOr<void> encode(Encoder&, Web::HTML::SerializedPolicyContainer const&);
template<>
ErrorOr<Web::HTML::SerializedPolicyContainer> decode(Decoder&);
}

View file

@ -18,6 +18,7 @@ void SessionHistoryEntry::visit_edges(Cell::Visitor& visitor)
Base::visit_edges(visitor);
visitor.visit(m_document_state);
visitor.visit(m_original_source_browsing_context);
visitor.visit(m_policy_container);
}
SessionHistoryEntry::SessionHistoryEntry()

View file

@ -69,8 +69,8 @@ public:
[[nodiscard]] ScrollRestorationMode scroll_restoration_mode() const { return m_scroll_restoration_mode; }
void set_scroll_restoration_mode(ScrollRestorationMode scroll_restoration_mode) { m_scroll_restoration_mode = scroll_restoration_mode; }
[[nodiscard]] Optional<PolicyContainer> const& policy_container() const { return m_policy_container; }
void set_policy_container(Optional<PolicyContainer> policy_container) { m_policy_container = move(policy_container); }
[[nodiscard]] GC::Ptr<PolicyContainer> policy_container() const { return m_policy_container; }
void set_policy_container(GC::Ptr<PolicyContainer> policy_container) { m_policy_container = policy_container; }
[[nodiscard]] Optional<ByteString> const& browsing_context_name() const { return m_browsing_context_name; }
void set_browsing_context_name(Optional<ByteString> browsing_context_name) { m_browsing_context_name = move(browsing_context_name); }
@ -111,7 +111,7 @@ private:
ScrollRestorationMode m_scroll_restoration_mode { ScrollRestorationMode::Auto };
// policy container, a policy container or null
Optional<PolicyContainer> m_policy_container;
GC::Ptr<PolicyContainer> m_policy_container;
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#she-scroll-position
// FIXME: scroll position data, which is scroll position data for the document's restorable scrollable regions

View file

@ -0,0 +1,22 @@
/*
* Copyright (c) 2025, Luke Wilde <luke@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/HTML/PolicyContainers.h>
#include <LibWeb/HTML/Scripting/Environments.h>
#include <LibWeb/HTML/SourceSnapshotParams.h>
namespace Web::HTML {
GC_DEFINE_ALLOCATOR(SourceSnapshotParams);
void SourceSnapshotParams::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(fetch_client);
visitor.visit(source_policy_container);
}
}

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2023, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
* Copyright (c) 2025, Luke Wilde <luke@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -12,7 +13,22 @@
namespace Web::HTML {
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#source-snapshot-params
struct SourceSnapshotParams {
struct SourceSnapshotParams : public JS::Cell {
GC_CELL(SourceSnapshotParams, JS::Cell)
GC_DECLARE_ALLOCATOR(SourceSnapshotParams);
public:
SourceSnapshotParams(bool has_transient_activation, SandboxingFlagSet sandboxing_flags, bool allows_downloading, GC::Ref<EnvironmentSettingsObject> fetch_client, GC::Ref<PolicyContainer> source_policy_container)
: has_transient_activation(has_transient_activation)
, sandboxing_flags(sandboxing_flags)
, allows_downloading(allows_downloading)
, fetch_client(fetch_client)
, source_policy_container(source_policy_container)
{
}
virtual ~SourceSnapshotParams() = default;
// a boolean
bool has_transient_activation;
@ -23,10 +39,13 @@ struct SourceSnapshotParams {
bool allows_downloading;
// an environment settings object, only to be used as a request client
GC::Ptr<EnvironmentSettingsObject> fetch_client;
GC::Ref<EnvironmentSettingsObject> fetch_client;
// a policy container
PolicyContainer source_policy_container;
GC::Ref<PolicyContainer> source_policy_container;
protected:
virtual void visit_edges(Cell::Visitor&) override;
};
}

View file

@ -438,7 +438,7 @@ GC_DEFINE_ALLOCATOR(ChangingNavigableContinuationState);
TraversableNavigable::HistoryStepResult TraversableNavigable::apply_the_history_step(
int step,
bool check_for_cancelation,
IGNORE_USE_IN_ESCAPING_LAMBDA Optional<SourceSnapshotParams> source_snapshot_params,
GC::Ptr<SourceSnapshotParams> source_snapshot_params,
GC::Ptr<Navigable> initiator_to_check,
IGNORE_USE_IN_ESCAPING_LAMBDA UserNavigationInvolvement user_involvement,
IGNORE_USE_IN_ESCAPING_LAMBDA Optional<Bindings::NavigationType> navigation_type,
@ -456,7 +456,7 @@ TraversableNavigable::HistoryStepResult TraversableNavigable::apply_the_history_
// 3. If initiatorToCheck is not null, then:
if (initiator_to_check != nullptr) {
// 1. Assert: sourceSnapshotParams is not null.
VERIFY(source_snapshot_params.has_value());
VERIFY(source_snapshot_params);
// 2. For each navigable of get all navigables whose current session history entry will change or reload:
// if initiatorToCheck is not allowed by sandboxing to navigate navigable given sourceSnapshotParams, then return "initiator-disallowed".
@ -628,10 +628,10 @@ TraversableNavigable::HistoryStepResult TraversableNavigable::apply_the_history_
auto target_snapshot_params = navigable->snapshot_target_snapshot_params();
// 3. Let potentiallyTargetSpecificSourceSnapshotParams be sourceSnapshotParams.
Optional<SourceSnapshotParams> potentially_target_specific_source_snapshot_params = source_snapshot_params;
GC::Ptr<SourceSnapshotParams> potentially_target_specific_source_snapshot_params = source_snapshot_params;
// 4. If potentiallyTargetSpecificSourceSnapshotParams is null, then set it to the result of snapshotting source snapshot params given navigable's active document.
if (!potentially_target_specific_source_snapshot_params.has_value()) {
if (!potentially_target_specific_source_snapshot_params) {
potentially_target_specific_source_snapshot_params = navigable->active_document()->snapshot_source_snapshot_params();
}
@ -1134,17 +1134,17 @@ bool TraversableNavigable::can_go_forward() const
}
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#traverse-the-history-by-a-delta
void TraversableNavigable::traverse_the_history_by_delta(int delta, Optional<DOM::Document&> source_document)
void TraversableNavigable::traverse_the_history_by_delta(int delta, GC::Ptr<DOM::Document> source_document)
{
// 1. Let sourceSnapshotParams and initiatorToCheck be null.
Optional<SourceSnapshotParams> source_snapshot_params = {};
GC::Ptr<SourceSnapshotParams> source_snapshot_params = nullptr;
GC::Ptr<Navigable> initiator_to_check = nullptr;
// 2. Let userInvolvement be "browser UI".
UserNavigationInvolvement user_involvement = UserNavigationInvolvement::BrowserUI;
// 1. If sourceDocument is given, then:
if (source_document.has_value()) {
if (source_document) {
// 1. Set sourceSnapshotParams to the result of snapshotting source snapshot params given sourceDocument.
source_snapshot_params = source_document->snapshot_source_snapshot_params();
@ -1156,7 +1156,7 @@ void TraversableNavigable::traverse_the_history_by_delta(int delta, Optional<DOM
}
// 4. Append the following session history traversal steps to traversable:
append_session_history_traversal_steps(GC::create_function(heap(), [this, delta, source_snapshot_params = move(source_snapshot_params), initiator_to_check, user_involvement] {
append_session_history_traversal_steps(GC::create_function(heap(), [this, delta, source_snapshot_params, initiator_to_check, user_involvement] {
// 1. Let allSteps be the result of getting all used history steps for traversable.
auto all_steps = get_all_used_history_steps();
@ -1205,10 +1205,10 @@ TraversableNavigable::HistoryStepResult TraversableNavigable::apply_the_push_or_
return apply_the_history_step(step, false, {}, {}, user_involvement, navigation_type, synchronous_navigation);
}
TraversableNavigable::HistoryStepResult TraversableNavigable::apply_the_traverse_history_step(int step, Optional<SourceSnapshotParams> source_snapshot_params, GC::Ptr<Navigable> initiator_to_check, UserNavigationInvolvement user_involvement)
TraversableNavigable::HistoryStepResult TraversableNavigable::apply_the_traverse_history_step(int step, GC::Ptr<SourceSnapshotParams> source_snapshot_params, GC::Ptr<Navigable> initiator_to_check, UserNavigationInvolvement user_involvement)
{
// 1. Return the result of applying the history step step to traversable given true, sourceSnapshotParams, initiatorToCheck, userInvolvement, and "traverse".
return apply_the_history_step(step, true, move(source_snapshot_params), initiator_to_check, user_involvement, Bindings::NavigationType::Traverse, SynchronousNavigation::No);
return apply_the_history_step(step, true, source_snapshot_params, initiator_to_check, user_involvement, Bindings::NavigationType::Traverse, SynchronousNavigation::No);
}
// https://html.spec.whatwg.org/multipage/document-sequences.html#close-a-top-level-traversable

View file

@ -60,7 +60,7 @@ public:
Applied,
};
HistoryStepResult apply_the_traverse_history_step(int, Optional<SourceSnapshotParams>, GC::Ptr<Navigable>, UserNavigationInvolvement);
HistoryStepResult apply_the_traverse_history_step(int, GC::Ptr<SourceSnapshotParams>, GC::Ptr<Navigable>, UserNavigationInvolvement);
HistoryStepResult apply_the_reload_history_step(UserNavigationInvolvement);
enum class SynchronousNavigation : bool {
Yes,
@ -76,7 +76,7 @@ public:
Vector<int> get_all_used_history_steps() const;
void clear_the_forward_session_history();
void traverse_the_history_by_delta(int delta, Optional<DOM::Document&> source_document = {});
void traverse_the_history_by_delta(int delta, GC::Ptr<DOM::Document> source_document = {});
void close_top_level_traversable();
void definitely_close_top_level_traversable();
@ -125,7 +125,7 @@ private:
HistoryStepResult apply_the_history_step(
int step,
bool check_for_cancelation,
Optional<SourceSnapshotParams>,
GC::Ptr<SourceSnapshotParams>,
GC::Ptr<Navigable> initiator_to_check,
UserNavigationInvolvement user_involvement,
Optional<Bindings::NavigationType> navigation_type,

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org>
* Copyright (c) 2025, Luke Wilde <luke@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -52,6 +53,7 @@ void WorkerGlobalScope::visit_edges(Cell::Visitor& visitor)
visitor.visit(m_internal_port);
visitor.visit(m_page);
visitor.visit(m_fonts);
visitor.visit(m_policy_container);
}
void WorkerGlobalScope::finalize()
@ -165,4 +167,13 @@ GC::Ref<CSS::FontFaceSet> WorkerGlobalScope::fonts()
return *m_fonts;
}
GC::Ref<PolicyContainer> WorkerGlobalScope::policy_container() const
{
auto& realm = this->realm();
if (!m_policy_container) {
m_policy_container = realm.create<PolicyContainer>(realm);
}
return *m_policy_container;
}
}

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org>
* Copyright (c) 2025, Luke Wilde <luke@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -92,7 +93,7 @@ public:
Web::Page* page() { return m_page.ptr(); }
PolicyContainer policy_container() const { return m_policy_container; }
GC::Ref<PolicyContainer> policy_container() const;
bool is_closing() const { return m_closing; }
@ -137,7 +138,7 @@ private:
// https://html.spec.whatwg.org/multipage/workers.html#concept-workerglobalscope-policy-container
// A WorkerGlobalScope object has an associated policy container (a policy container). It is initially a new policy container.
PolicyContainer m_policy_container;
mutable GC::Ptr<PolicyContainer> m_policy_container;
// https://html.spec.whatwg.org/multipage/workers.html#concept-workerglobalscope-embedder-policy
// A WorkerGlobalScope object has an associated embedder policy (an embedder policy).

View file

@ -40,7 +40,7 @@ ErrorOr<GC::Ref<SVGDecodedImageData>> SVGDecodedImageData::create(JS::Realm& rea
navigation_params->navigable = navigable;
navigation_params->response = response;
navigation_params->origin = URL::Origin {};
navigation_params->policy_container = HTML::PolicyContainer {};
navigation_params->policy_container = navigable->heap().allocate<HTML::PolicyContainer>(navigable->active_document()->realm());
navigation_params->final_sandboxing_flag_set = HTML::SandboxingFlagSet {};
navigation_params->opener_policy = HTML::OpenerPolicy {};

View file

@ -76,7 +76,7 @@ void DedicatedWorkerHost::run(GC::Ref<Web::Page> page, Web::HTML::TransferDataHo
// FIXME: support for 'owner' set on WorkerGlobalScope
// IMPLEMENTATION DEFINED: We need an object to represent the fetch response's client
auto outside_settings = inner_settings->realm().create<Web::HTML::EnvironmentSettingsSnapshot>(inner_settings->realm_execution_context().copy(), outside_settings_snapshot);
auto outside_settings = inner_settings->realm().create<Web::HTML::EnvironmentSettingsSnapshot>(inner_settings->realm(), inner_settings->realm_execution_context().copy(), outside_settings_snapshot);
// 12. If is shared is true, then:
if (is_shared) {