mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-07 00:29:15 +00:00
LibWeb/Fetch: Deserialize abort reason
Deserialize the fetch controllers abort reason according to the spec. Currently fetch does not support a scenario where this actually happens.
This commit is contained in:
parent
2b1725ea51
commit
b263cd11f7
Notes:
github-actions[bot]
2024-12-16 11:44:20 +00:00
Author: https://github.com/skyz1
Commit: b263cd11f7
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/1871
Reviewed-by: https://github.com/ADKaster
Reviewed-by: https://github.com/BroadbentJim
3 changed files with 66 additions and 12 deletions
|
@ -73,14 +73,14 @@ GC::Ref<WebIDL::Promise> fetch(JS::VM& vm, RequestInfo const& input, RequestInit
|
||||||
auto locally_aborted = Fetching::RefCountedFlag::create(false);
|
auto locally_aborted = Fetching::RefCountedFlag::create(false);
|
||||||
|
|
||||||
// 10. Let controller be null.
|
// 10. Let controller be null.
|
||||||
GC::Ptr<Infrastructure::FetchController> controller;
|
auto controller_holder = Infrastructure::FetchControllerHolder::create(vm);
|
||||||
|
|
||||||
// NOTE: Step 11 is done out of order so that the controller is non-null when we capture the GCPtr by copy in the abort algorithm lambda.
|
// NOTE: Step 11 is done out of order so that the controller is non-null when we capture the GCPtr by copy in the abort algorithm lambda.
|
||||||
// This is not observable, AFAICT.
|
// This is not observable, AFAICT.
|
||||||
|
|
||||||
// 12. Set controller to the result of calling fetch given request and processResponse given response being these
|
// 12. Set controller to the result of calling fetch given request and processResponse given response being these
|
||||||
// steps:
|
// steps:
|
||||||
auto process_response = [locally_aborted, promise_capability, request, response_object, &relevant_realm](GC::Ref<Infrastructure::Response> response) mutable {
|
auto process_response = [locally_aborted, promise_capability, request, response_object, controller_holder, &relevant_realm](GC::Ref<Infrastructure::Response> response) mutable {
|
||||||
// 1. If locallyAborted is true, then abort these steps.
|
// 1. If locallyAborted is true, then abort these steps.
|
||||||
if (locally_aborted->value())
|
if (locally_aborted->value())
|
||||||
return;
|
return;
|
||||||
|
@ -90,9 +90,9 @@ GC::Ref<WebIDL::Promise> fetch(JS::VM& vm, RequestInfo const& input, RequestInit
|
||||||
|
|
||||||
// 2. If response’s aborted flag is set, then:
|
// 2. If response’s aborted flag is set, then:
|
||||||
if (response->aborted()) {
|
if (response->aborted()) {
|
||||||
// FIXME: 1. Let deserializedError be the result of deserialize a serialized abort reason given controller’s
|
// 1. Let deserializedError be the result of deserialize a serialized abort reason given controller’s
|
||||||
// serialized abort reason and relevantRealm.
|
// serialized abort reason and relevantRealm.
|
||||||
auto deserialized_error = JS::js_undefined();
|
auto deserialized_error = controller_holder->controller()->deserialize_a_serialized_abort_reason(relevant_realm);
|
||||||
|
|
||||||
// 2. Abort the fetch() call with p, request, responseObject, and deserializedError.
|
// 2. Abort the fetch() call with p, request, responseObject, and deserializedError.
|
||||||
abort_fetch(relevant_realm, promise_capability, request, response_object, deserialized_error);
|
abort_fetch(relevant_realm, promise_capability, request, response_object, deserialized_error);
|
||||||
|
@ -115,7 +115,7 @@ GC::Ref<WebIDL::Promise> fetch(JS::VM& vm, RequestInfo const& input, RequestInit
|
||||||
// 5. Resolve p with responseObject.
|
// 5. Resolve p with responseObject.
|
||||||
WebIDL::resolve_promise(relevant_realm, promise_capability, response_object);
|
WebIDL::resolve_promise(relevant_realm, promise_capability, response_object);
|
||||||
};
|
};
|
||||||
controller = MUST(Fetching::fetch(
|
controller_holder->set_controller(MUST(Fetching::fetch(
|
||||||
realm,
|
realm,
|
||||||
request,
|
request,
|
||||||
Infrastructure::FetchAlgorithms::create(vm,
|
Infrastructure::FetchAlgorithms::create(vm,
|
||||||
|
@ -126,20 +126,20 @@ GC::Ref<WebIDL::Promise> fetch(JS::VM& vm, RequestInfo const& input, RequestInit
|
||||||
.process_response = move(process_response),
|
.process_response = move(process_response),
|
||||||
.process_response_end_of_body = {},
|
.process_response_end_of_body = {},
|
||||||
.process_response_consume_body = {},
|
.process_response_consume_body = {},
|
||||||
})));
|
}))));
|
||||||
|
|
||||||
// 11. Add the following abort steps to requestObject’s signal:
|
// 11. Add the following abort steps to requestObject’s signal:
|
||||||
request_object->signal()->add_abort_algorithm([locally_aborted, request, controller, promise_capability, request_object, response_object, &relevant_realm] {
|
request_object->signal()->add_abort_algorithm([locally_aborted, request, controller_holder, promise_capability, request_object, response_object, &relevant_realm] {
|
||||||
dbgln_if(WEB_FETCH_DEBUG, "Fetch: Request object signal's abort algorithm called");
|
dbgln_if(WEB_FETCH_DEBUG, "Fetch: Request object signal's abort algorithm called");
|
||||||
|
|
||||||
// 1. Set locallyAborted to true.
|
// 1. Set locallyAborted to true.
|
||||||
locally_aborted->set_value(true);
|
locally_aborted->set_value(true);
|
||||||
|
|
||||||
// 2. Assert: controller is non-null.
|
// 2. Assert: controller is non-null.
|
||||||
VERIFY(controller);
|
VERIFY(controller_holder->controller());
|
||||||
|
|
||||||
// 3. Abort controller with requestObject’s signal’s abort reason.
|
// 3. Abort controller with requestObject’s signal’s abort reason.
|
||||||
controller->abort(relevant_realm, request_object->signal()->reason());
|
controller_holder->controller()->abort(relevant_realm, request_object->signal()->reason());
|
||||||
|
|
||||||
// AD-HOC: An execution context is required for Promise functions.
|
// AD-HOC: An execution context is required for Promise functions.
|
||||||
HTML::TemporaryExecutionContext execution_context { relevant_realm };
|
HTML::TemporaryExecutionContext execution_context { relevant_realm };
|
||||||
|
|
|
@ -96,7 +96,27 @@ void FetchController::abort(JS::Realm& realm, Optional<JS::Value> error)
|
||||||
m_serialized_abort_reason = structured_serialize(realm.vm(), error.value(), fallback_error);
|
m_serialized_abort_reason = structured_serialize(realm.vm(), error.value(), fallback_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: https://fetch.spec.whatwg.org/#deserialize-a-serialized-abort-reason
|
// https://fetch.spec.whatwg.org/#deserialize-a-serialized-abort-reason
|
||||||
|
JS::Value FetchController::deserialize_a_serialized_abort_reason(JS::Realm& realm)
|
||||||
|
{
|
||||||
|
// 1. Let fallbackError be an "AbortError" DOMException.
|
||||||
|
auto fallback_error = WebIDL::AbortError::create(realm, "Fetch was aborted"_string);
|
||||||
|
|
||||||
|
// 2. Let deserializedError be fallbackError.
|
||||||
|
JS::Value deserialized_error = fallback_error;
|
||||||
|
|
||||||
|
// 3. If abortReason is non-null, then set deserializedError to StructuredDeserialize(abortReason, realm).
|
||||||
|
// If that threw an exception or returned undefined, then set deserializedError to fallbackError.
|
||||||
|
if (m_serialized_abort_reason.has_value()) {
|
||||||
|
auto deserialized_error_or_exception = HTML::structured_deserialize(realm.vm(), m_serialized_abort_reason.value(), realm, {});
|
||||||
|
if (!deserialized_error_or_exception.is_exception() && !deserialized_error_or_exception.value().is_undefined()) {
|
||||||
|
deserialized_error = deserialized_error_or_exception.value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Return deserializedError.
|
||||||
|
return deserialized_error;
|
||||||
|
}
|
||||||
|
|
||||||
// https://fetch.spec.whatwg.org/#fetch-controller-terminate
|
// https://fetch.spec.whatwg.org/#fetch-controller-terminate
|
||||||
void FetchController::terminate()
|
void FetchController::terminate()
|
||||||
|
@ -137,4 +157,19 @@ void FetchController::fetch_task_complete(u64 fetch_task_id)
|
||||||
m_ongoing_fetch_tasks.remove(fetch_task_id);
|
m_ongoing_fetch_tasks.remove(fetch_task_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GC_DEFINE_ALLOCATOR(FetchControllerHolder);
|
||||||
|
|
||||||
|
FetchControllerHolder::FetchControllerHolder() = default;
|
||||||
|
|
||||||
|
GC::Ref<FetchControllerHolder> FetchControllerHolder::create(JS::VM& vm)
|
||||||
|
{
|
||||||
|
return vm.heap().allocate<FetchControllerHolder>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FetchControllerHolder::visit_edges(JS::Cell::Visitor& visitor)
|
||||||
|
{
|
||||||
|
Base::visit_edges(visitor);
|
||||||
|
visitor.visit(m_controller);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@ public:
|
||||||
void process_next_manual_redirect() const;
|
void process_next_manual_redirect() const;
|
||||||
[[nodiscard]] GC::Ref<FetchTimingInfo> extract_full_timing_info() const;
|
[[nodiscard]] GC::Ref<FetchTimingInfo> extract_full_timing_info() const;
|
||||||
void abort(JS::Realm&, Optional<JS::Value>);
|
void abort(JS::Realm&, Optional<JS::Value>);
|
||||||
|
JS::Value deserialize_a_serialized_abort_reason(JS::Realm&);
|
||||||
void terminate();
|
void terminate();
|
||||||
|
|
||||||
void set_fetch_params(Badge<FetchParams>, GC::Ref<FetchParams> fetch_params) { m_fetch_params = fetch_params; }
|
void set_fetch_params(Badge<FetchParams>, GC::Ref<FetchParams> fetch_params) { m_fetch_params = fetch_params; }
|
||||||
|
@ -77,7 +78,7 @@ private:
|
||||||
// https://fetch.spec.whatwg.org/#fetch-controller-report-timing-steps
|
// https://fetch.spec.whatwg.org/#fetch-controller-report-timing-steps
|
||||||
// serialized abort reason (default null)
|
// serialized abort reason (default null)
|
||||||
// Null or a Record (result of StructuredSerialize).
|
// Null or a Record (result of StructuredSerialize).
|
||||||
HTML::SerializationRecord m_serialized_abort_reason;
|
Optional<HTML::SerializationRecord> m_serialized_abort_reason;
|
||||||
|
|
||||||
// https://fetch.spec.whatwg.org/#fetch-controller-next-manual-redirect-steps
|
// https://fetch.spec.whatwg.org/#fetch-controller-next-manual-redirect-steps
|
||||||
// next manual redirect steps (default null)
|
// next manual redirect steps (default null)
|
||||||
|
@ -90,4 +91,22 @@ private:
|
||||||
u64 m_next_fetch_task_id { 0 };
|
u64 m_next_fetch_task_id { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class FetchControllerHolder : public JS::Cell {
|
||||||
|
GC_CELL(FetchControllerHolder, JS::Cell);
|
||||||
|
GC_DECLARE_ALLOCATOR(FetchControllerHolder);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static GC::Ref<FetchControllerHolder> create(JS::VM&);
|
||||||
|
|
||||||
|
[[nodiscard]] GC::Ptr<FetchController> const& controller() const { return m_controller; }
|
||||||
|
void set_controller(GC::Ref<FetchController> controller) { m_controller = controller; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
FetchControllerHolder();
|
||||||
|
|
||||||
|
virtual void visit_edges(Cell::Visitor&) override;
|
||||||
|
|
||||||
|
GC::Ptr<FetchController> m_controller;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue