diff --git a/Userland/Libraries/LibWeb/HTML/Navigable.cpp b/Userland/Libraries/LibWeb/HTML/Navigable.cpp
index 477c7d42c8..b11db3a0b5 100644
--- a/Userland/Libraries/LibWeb/HTML/Navigable.cpp
+++ b/Userland/Libraries/LibWeb/HTML/Navigable.cpp
@@ -23,6 +23,27 @@
namespace Web::HTML {
+class ResponseHolder : public JS::Cell {
+ JS_CELL(ResponseHolder, JS::Cell);
+
+public:
+ [[nodiscard]] static JS::NonnullGCPtr create(JS::VM& vm)
+ {
+ return vm.heap().allocate_without_realm();
+ }
+
+ [[nodiscard]] JS::GCPtr response() const { return m_response; }
+ void set_response(JS::GCPtr response) { m_response = response; }
+
+ virtual void visit_edges(Cell::Visitor& visitor) override
+ {
+ visitor.visit(m_response);
+ }
+
+private:
+ JS::GCPtr m_response;
+};
+
static HashTable& all_navigables()
{
static HashTable set;
@@ -458,7 +479,9 @@ static WebIDL::ExceptionOr> create_navigation_params_
request->set_history_navigation(true);
// 9. Let response be null.
- JS::GCPtr response = nullptr;
+ // NOTE: We use a heap-allocated cell to hold the response pointer because the processResponse callback below
+ // might use it after this stack is freed.
+ auto response_holder = ResponseHolder::create(vm);
// 10. Let responseOrigin be null.
Optional response_origin;
@@ -484,7 +507,7 @@ static WebIDL::ExceptionOr> create_navigation_params_
// FIXME: 3. If the result of should navigation request of type be blocked by Content Security Policy? given request and cspNavigationType is "Blocked", then set response to a network error and break. [CSP]
// 4. Set response to null.
- response = nullptr;
+ response_holder->set_response(nullptr);
// 5. If fetchController is null, then set fetchController to the result of fetching request,
// with processEarlyHintsResponse set to processEarlyHintsResponseas defined below, processResponse
@@ -493,9 +516,9 @@ static WebIDL::ExceptionOr> create_navigation_params_
// FIXME: Let processEarlyHintsResponse be the following algorithm given a response earlyResponse:
// Let processResponse be the following algorithm given a response fetchedResponse:
- auto process_response = [&response](JS::NonnullGCPtr fetch_response) {
+ auto process_response = [response_holder](JS::NonnullGCPtr fetch_response) {
// 1. Set response to fetchedResponse.
- response = fetch_response;
+ response_holder->set_response(fetch_response);
};
fetch_controller = TRY(Fetch::Fetching::fetch(
@@ -519,7 +542,7 @@ static WebIDL::ExceptionOr> create_navigation_params_
// 7. Wait until either response is non-null, or navigable's ongoing navigation changes to no longer equal navigationId.
Platform::EventLoopPlugin::the().spin_until([&]() {
- if (response != nullptr)
+ if (response_holder->response() != nullptr)
return true;
if (navigation_id.has_value() && (!navigable->ongoing_navigation().has() || navigable->ongoing_navigation().get() != *navigation_id))
@@ -540,10 +563,10 @@ static WebIDL::ExceptionOr> create_navigation_params_
// 11. Set responseOrigin to the result of determining the origin given response's URL, finalSandboxFlags,
// entry's document state's initiator origin, and null.
- response_origin = determine_the_origin(*response->url(), final_sandbox_flags, entry->document_state->initiator_origin(), {});
+ response_origin = determine_the_origin(*response_holder->response()->url(), final_sandbox_flags, entry->document_state->initiator_origin(), {});
// 14. Set locationURL to response's location URL given currentURL's fragment.
- auto location_url = response->location_url(current_url.fragment());
+ auto location_url = response_holder->response()->location_url(current_url.fragment());
VERIFY(!location_url.is_error());
@@ -604,13 +627,13 @@ static WebIDL::ExceptionOr> create_navigation_params_
// - locationURL is failure; or
// - locationURL is a URL whose scheme is a fetch scheme
// then return null.
- if (response->is_network_error() || location_url.is_error() || (location_url.value().has_value() && Fetch::Infrastructure::is_fetch_scheme(location_url.value().value().scheme()))) {
+ if (response_holder->response()->is_network_error() || location_url.is_error() || (location_url.value().has_value() && Fetch::Infrastructure::is_fetch_scheme(location_url.value().value().scheme()))) {
return OptionalNone {};
}
// 22. Assert: locationURL is null and response is not a network error.
VERIFY(!location_url.value().has_value());
- VERIFY(!response->is_network_error());
+ VERIFY(!response_holder->response()->is_network_error());
// FIXME: 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,
@@ -633,7 +656,7 @@ static WebIDL::ExceptionOr> create_navigation_params_
HTML::NavigationParams navigation_params {
.id = navigation_id,
.request = request,
- .response = *response,
+ .response = *response_holder->response(),
.origin = *response_origin,
.policy_container = PolicyContainer {},
.final_sandboxing_flag_set = SandboxingFlagSet {},