LibWeb: Replace request's "window" with "traversable for user prompts"

User prompts are not tied to specific Windows or the client's Window.
They are tied to a traversable navigable (browser tab).
This commit is contained in:
Kenneth Myhra 2025-08-07 10:54:40 +02:00 committed by Sam Atkins
commit 70cafc558e
Notes: github-actions[bot] 2025-08-08 10:14:14 +00:00
5 changed files with 41 additions and 38 deletions

View file

@ -421,9 +421,9 @@ void Violation::report_a_violation(JS::Realm& realm)
auto& environment_settings_object = Bindings::principal_host_defined_environment_settings_object(HTML::principal_realm(realm)); auto& environment_settings_object = Bindings::principal_host_defined_environment_settings_object(HTML::principal_realm(realm));
request->set_origin(environment_settings_object.origin()); request->set_origin(environment_settings_object.origin());
// window // traversable for user prompts
// "no-window" // "no-traversable"
request->set_window(Fetch::Infrastructure::Request::Window::NoWindow); request->set_traversable_for_user_prompts(Fetch::Infrastructure::Request::TraversableForUserPrompts::NoTraversable);
// client // client
// violation's global object's relevant settings object // violation's global object's relevant settings object

View file

@ -132,12 +132,12 @@ WebIDL::ExceptionOr<GC::Ref<Infrastructure::FetchController>> fetch(JS::Realm& r
// 9. If requests window is "client", then set requests window to requests client, if requests clients global // 9. If requests window is "client", then set requests window to requests client, if requests clients global
// object is a Window object; otherwise "no-window". // object is a Window object; otherwise "no-window".
auto const* window = request.window().get_pointer<Infrastructure::Request::Window>(); auto const* window = request.traversable_for_user_prompts().get_pointer<Infrastructure::Request::TraversableForUserPrompts>();
if (window && *window == Infrastructure::Request::Window::Client) { if (window && *window == Infrastructure::Request::TraversableForUserPrompts::Client) {
if (is<HTML::Window>(request.client()->global_object())) { if (is<HTML::Window>(request.client()->global_object())) {
request.set_window(request.client()); request.set_traversable_for_user_prompts(request.client());
} else { } else {
request.set_window(Infrastructure::Request::Window::NoWindow); request.set_traversable_for_user_prompts(Infrastructure::Request::TraversableForUserPrompts::NoTraversable);
} }
} }
@ -153,7 +153,7 @@ WebIDL::ExceptionOr<GC::Ref<Infrastructure::FetchController>> fetch(JS::Realm& r
// - requests mode is "same-origin", "cors", or "no-cors" // - requests mode is "same-origin", "cors", or "no-cors"
&& (request.mode() == Infrastructure::Request::Mode::SameOrigin || request.mode() == Infrastructure::Request::Mode::CORS || request.mode() == Infrastructure::Request::Mode::NoCORS) && (request.mode() == Infrastructure::Request::Mode::SameOrigin || request.mode() == Infrastructure::Request::Mode::CORS || request.mode() == Infrastructure::Request::Mode::NoCORS)
// - requests window is an environment settings object // - requests window is an environment settings object
&& request.window().has<GC::Ptr<HTML::EnvironmentSettingsObject>>() && request.traversable_for_user_prompts().has<GC::Ptr<HTML::EnvironmentSettingsObject>>()
// - requests method is `GET` // - requests method is `GET`
&& StringView { request.method() }.equals_ignoring_ascii_case("GET"sv) && StringView { request.method() }.equals_ignoring_ascii_case("GET"sv)
// - requests unsafe-request flag is not set or requests header list is empty // - requests unsafe-request flag is not set or requests header list is empty
@ -1707,10 +1707,10 @@ WebIDL::ExceptionOr<GC::Ref<PendingResponse>> http_network_or_cache_fetch(JS::Re
aborted = true; aborted = true;
}; };
// 1. If requests window is "no-window" and requests redirect mode is "error", then set httpFetchParams to // 1. If requests traversable for user prompts is "no-traversable" and requests redirect mode is "error",
// fetchParams and httpRequest to request. // then set httpFetchParams to fetchParams and httpRequest to request.
if (request->window().has<Infrastructure::Request::Window>() if (request->traversable_for_user_prompts().has<Infrastructure::Request::TraversableForUserPrompts>()
&& request->window().get<Infrastructure::Request::Window>() == Infrastructure::Request::Window::NoWindow && request->traversable_for_user_prompts().get<Infrastructure::Request::TraversableForUserPrompts>() == Infrastructure::Request::TraversableForUserPrompts::NoTraversable
&& request->redirect_mode() == Infrastructure::Request::RedirectMode::Error) { && request->redirect_mode() == Infrastructure::Request::RedirectMode::Error) {
http_fetch_params = fetch_params; http_fetch_params = fetch_params;
http_request = request; http_request = request;
@ -2123,11 +2123,11 @@ WebIDL::ExceptionOr<GC::Ref<PendingResponse>> http_network_or_cache_fetch(JS::Re
auto inner_pending_response = PendingResponse::create(vm, request, *response); auto inner_pending_response = PendingResponse::create(vm, request, *response);
// 14. If responses status is 401, httpRequests response tainting is not "cors", includeCredentials is true, // 14. If responses status is 401, httpRequests response tainting is not "cors", includeCredentials is true,
// and requests window is an environment settings object, then: // and requests traversable for user prompts is a traversable navigable:
if (response->status() == 401 if (response->status() == 401
&& http_request->response_tainting() != Infrastructure::Request::ResponseTainting::CORS && http_request->response_tainting() != Infrastructure::Request::ResponseTainting::CORS
&& include_credentials == IncludeCredentials::Yes && include_credentials == IncludeCredentials::Yes
&& request->window().has<GC::Ptr<HTML::EnvironmentSettingsObject>>() && request->traversable_for_user_prompts().has<GC::Ptr<HTML::TraversableNavigable>>()
// AD-HOC: Require at least one WWW-Authenticate header to be set before automatically retrying an authenticated // AD-HOC: Require at least one WWW-Authenticate header to be set before automatically retrying an authenticated
// request (see rule 1 below). See: https://github.com/whatwg/fetch/issues/1766 // request (see rule 1 below). See: https://github.com/whatwg/fetch/issues/1766
&& request->header_list()->contains("WWW-Authenticate"sv.bytes())) { && request->header_list()->contains("WWW-Authenticate"sv.bytes())) {
@ -2181,9 +2181,9 @@ WebIDL::ExceptionOr<GC::Ref<PendingResponse>> http_network_or_cache_fetch(JS::Re
dbgln_if(WEB_FETCH_DEBUG, "Fetch: Running 'HTTP network-or-cache fetch' inner_pending_response load callback"); dbgln_if(WEB_FETCH_DEBUG, "Fetch: Running 'HTTP network-or-cache fetch' inner_pending_response load callback");
// 15. If responses status is 407, then: // 15. If responses status is 407, then:
if (response->status() == 407) { if (response->status() == 407) {
// 1. If requests window is "no-window", then return a network error. // 1. If requests traversable for user prompts is "no-traversable", then return a network error.
if (request->window().has<Infrastructure::Request::Window>() if (request->traversable_for_user_prompts().has<Infrastructure::Request::TraversableForUserPrompts>()
&& request->window().get<Infrastructure::Request::Window>() == Infrastructure::Request::Window::NoWindow) { && request->traversable_for_user_prompts().get<Infrastructure::Request::TraversableForUserPrompts>() == Infrastructure::Request::TraversableForUserPrompts::NoTraversable) {
returned_pending_response->resolve(Infrastructure::Response::network_error(vm, "Request requires proxy authentication but has 'no-window' set"_string)); returned_pending_response->resolve(Infrastructure::Response::network_error(vm, "Request requires proxy authentication but has 'no-window' set"_string));
return; return;
} }

View file

@ -13,6 +13,7 @@
#include <LibWeb/DOMURL/DOMURL.h> #include <LibWeb/DOMURL/DOMURL.h>
#include <LibWeb/Fetch/Fetching/PendingResponse.h> #include <LibWeb/Fetch/Fetching/PendingResponse.h>
#include <LibWeb/Fetch/Infrastructure/HTTP/Requests.h> #include <LibWeb/Fetch/Infrastructure/HTTP/Requests.h>
#include <LibWeb/HTML/TraversableNavigable.h>
namespace Web::Fetch::Infrastructure { namespace Web::Fetch::Infrastructure {
@ -32,8 +33,9 @@ void Request::visit_edges(JS::Cell::Visitor& visitor)
[&](GC::Ref<Body>& body) { visitor.visit(body); }, [&](GC::Ref<Body>& body) { visitor.visit(body); },
[](auto&) {}); [](auto&) {});
visitor.visit(m_reserved_client); visitor.visit(m_reserved_client);
m_window.visit( m_traversable_for_user_prompts.visit(
[&](GC::Ptr<HTML::EnvironmentSettingsObject> const& value) { visitor.visit(value); }, [&](GC::Ptr<HTML::EnvironmentSettingsObject> const& value) { visitor.visit(value); },
[&](GC::Ptr<HTML::TraversableNavigable> const& value) { visitor.visit(value); },
[](auto const&) {}); [](auto const&) {});
visitor.visit(m_pending_responses); visitor.visit(m_pending_responses);
m_policy_container.visit( m_policy_container.visit(
@ -226,7 +228,7 @@ GC::Ref<Request> Request::clone(JS::Realm& realm) const
new_request->set_client(m_client); new_request->set_client(m_client);
new_request->set_reserved_client(m_reserved_client); new_request->set_reserved_client(m_reserved_client);
new_request->set_replaces_client_id(m_replaces_client_id); new_request->set_replaces_client_id(m_replaces_client_id);
new_request->set_window(m_window); new_request->set_traversable_for_user_prompts(m_traversable_for_user_prompts);
new_request->set_keepalive(m_keepalive); new_request->set_keepalive(m_keepalive);
new_request->set_initiator_type(m_initiator_type); new_request->set_initiator_type(m_initiator_type);
new_request->set_service_workers_mode(m_service_workers_mode); new_request->set_service_workers_mode(m_service_workers_mode);

View file

@ -147,8 +147,8 @@ public:
None, None,
}; };
enum class Window { enum class TraversableForUserPrompts {
NoWindow, NoTraversable,
Client, Client,
}; };
@ -173,7 +173,7 @@ public:
using PolicyContainerType = Variant<PolicyContainer, GC::Ref<HTML::PolicyContainer>>; using PolicyContainerType = Variant<PolicyContainer, GC::Ref<HTML::PolicyContainer>>;
using ReferrerType = Variant<Referrer, URL::URL>; using ReferrerType = Variant<Referrer, URL::URL>;
using ReservedClientType = GC::Ptr<HTML::Environment>; using ReservedClientType = GC::Ptr<HTML::Environment>;
using WindowType = Variant<Window, GC::Ptr<HTML::EnvironmentSettingsObject>>; using TraversableForUserPromptsType = Variant<TraversableForUserPrompts, GC::Ptr<HTML::EnvironmentSettingsObject>, GC::Ptr<HTML::TraversableNavigable>>;
[[nodiscard]] static GC::Ref<Request> create(JS::VM&); [[nodiscard]] static GC::Ref<Request> create(JS::VM&);
@ -204,8 +204,8 @@ public:
[[nodiscard]] String const& replaces_client_id() const { return m_replaces_client_id; } [[nodiscard]] String const& replaces_client_id() const { return m_replaces_client_id; }
void set_replaces_client_id(String replaces_client_id) { m_replaces_client_id = move(replaces_client_id); } void set_replaces_client_id(String replaces_client_id) { m_replaces_client_id = move(replaces_client_id); }
[[nodiscard]] WindowType const& window() const { return m_window; } [[nodiscard]] TraversableForUserPromptsType const& traversable_for_user_prompts() const { return m_traversable_for_user_prompts; }
void set_window(WindowType window) { m_window = move(window); } void set_traversable_for_user_prompts(TraversableForUserPromptsType traversable_for_user_prompts) { m_traversable_for_user_prompts = move(traversable_for_user_prompts); }
[[nodiscard]] bool keepalive() const { return m_keepalive; } [[nodiscard]] bool keepalive() const { return m_keepalive; }
void set_keepalive(bool keepalive) { m_keepalive = keepalive; } void set_keepalive(bool keepalive) { m_keepalive = keepalive; }
@ -373,9 +373,9 @@ private:
String m_replaces_client_id; String m_replaces_client_id;
// https://fetch.spec.whatwg.org/#concept-request-window // https://fetch.spec.whatwg.org/#concept-request-window
// A request has an associated window ("no-window", "client", or an environment settings object whose global object // A request has an associated traversable for user prompts, that is "no-traversable", "client", or a traversable
// is a Window object). Unless stated otherwise it is "client". // navigable. Unless stated otherwise it is "client".
WindowType m_window { Window::Client }; TraversableForUserPromptsType m_traversable_for_user_prompts { TraversableForUserPrompts::Client };
// https://fetch.spec.whatwg.org/#request-keepalive-flag // https://fetch.spec.whatwg.org/#request-keepalive-flag
// A request has an associated boolean keepalive. Unless stated otherwise it is false. // A request has an associated boolean keepalive. Unless stated otherwise it is false.

View file

@ -153,23 +153,24 @@ WebIDL::ExceptionOr<GC::Ref<Request>> Request::construct_impl(JS::Realm& realm,
// 7. Let origin be thiss relevant settings objects origin. // 7. Let origin be thiss relevant settings objects origin.
auto const& origin = HTML::relevant_settings_object(*request_object).origin(); auto const& origin = HTML::relevant_settings_object(*request_object).origin();
// 8. Let window be "client". // 8. Let traversableForUserPrompts be "client".
auto window = Infrastructure::Request::WindowType { Infrastructure::Request::Window::Client }; auto traversable_for_user_prompts = Infrastructure::Request::TraversableForUserPromptsType { Infrastructure::Request::TraversableForUserPrompts::Client };
// 9. If requests window is an environment settings object and its origin is same origin with origin, then set window to requests window. // 9. If requests traversable for user prompts is an environment settings object and its origin is same origin with
if (input_request->window().has<GC::Ptr<HTML::EnvironmentSettingsObject>>()) { // origin, then set traversableForUserPrompts to requests traversable for user prompts.
auto eso = input_request->window().get<GC::Ptr<HTML::EnvironmentSettingsObject>>(); if (input_request->traversable_for_user_prompts().has<GC::Ptr<HTML::EnvironmentSettingsObject>>()) {
auto eso = input_request->traversable_for_user_prompts().get<GC::Ptr<HTML::EnvironmentSettingsObject>>();
if (eso->origin().is_same_origin(origin)) if (eso->origin().is_same_origin(origin))
window = input_request->window(); traversable_for_user_prompts = input_request->traversable_for_user_prompts();
} }
// 10. If init["window"] exists and is non-null, then throw a TypeError. // 10. If init["window"] exists and is non-null, then throw a TypeError.
if (init.window.has_value() && !init.window->is_null()) if (init.window.has_value() && !init.window->is_null())
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "The 'window' property must be omitted or null"sv }; return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "The 'window' property must be omitted or null"sv };
// 11. If init["window"] exists, then set window to "no-window". // 11. If init["window"] exists, then set traversableForUserPrompts to "no-traversable".
if (init.window.has_value()) if (init.window.has_value())
window = Infrastructure::Request::Window::NoWindow; traversable_for_user_prompts = Infrastructure::Request::TraversableForUserPrompts::NoTraversable;
// 12. Set request to a new request with the following properties: // 12. Set request to a new request with the following properties:
// NOTE: This is done at the beginning as the 'this' value Request object // NOTE: This is done at the beginning as the 'this' value Request object
@ -199,9 +200,9 @@ WebIDL::ExceptionOr<GC::Ref<Request>> Request::construct_impl(JS::Realm& realm,
// Thiss relevant settings object. // Thiss relevant settings object.
request->set_client(&HTML::relevant_settings_object(*request_object)); request->set_client(&HTML::relevant_settings_object(*request_object));
// window // traversable for user prompts
// window. // traversableForUserPrompts.
request->set_window(window); request->set_traversable_for_user_prompts(traversable_for_user_prompts);
// priority // priority
// requests priority. // requests priority.