script: Do not start Fetch operations if they have been aborted by the AbortController (#39295)

The first step for aborting fetch calls. It only
has the case where the signal was already aborted
prior to fetch starting.

Part of #34866

Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
This commit is contained in:
Tim van der Lippe 2025-09-17 10:49:27 +02:00 committed by GitHub
parent 6deb42dbd5
commit 6cba44e0e3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 49 additions and 92 deletions

View file

@ -7,6 +7,8 @@ use std::sync::{Arc, Mutex};
use base::id::WebViewId;
use ipc_channel::ipc;
use js::jsval::UndefinedValue;
use js::rust::HandleValue;
use net_traits::policy_container::{PolicyContainer, RequestPolicyContainer};
use net_traits::request::{
CorsSettings, CredentialsMode, Destination, InsecureRequestsPolicy, Referrer,
@ -19,12 +21,14 @@ use net_traits::{
};
use servo_url::ServoUrl;
use crate::dom::bindings::codegen::Bindings::AbortSignalBinding::AbortSignalMethods;
use crate::dom::bindings::codegen::Bindings::RequestBinding::{
RequestInfo, RequestInit, RequestMethods,
};
use crate::dom::bindings::codegen::Bindings::ResponseBinding::Response_Binding::ResponseMethods;
use crate::dom::bindings::codegen::Bindings::ResponseBinding::ResponseType as DOMResponseType;
use crate::dom::bindings::error::Error;
use crate::dom::bindings::import::module::SafeJSContext;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
use crate::dom::bindings::reflector::DomGlobal;
@ -139,6 +143,27 @@ fn request_init_from_request(request: NetTraitsRequest) -> RequestBuilder {
}
}
/// <https://fetch.spec.whatwg.org/#abort-fetch>
fn abort_fetch_call(
promise: Rc<Promise>,
_request: &NetTraitsRequest,
_response_object: Option<&Response>,
abort_reason: HandleValue,
cx: SafeJSContext,
can_gc: CanGc,
) {
// Step 1. Reject promise with error.
promise.reject(cx, abort_reason, can_gc);
// Step 2. If requests body is non-null and is readable, then cancel requests body with error.
// TODO
// Step 3. If responseObject is null, then return.
// TODO
// Step 4. Let response be responseObjects response.
// TODO
// Step 5. If responses body is non-null and is readable, then error responses body with error.
// TODO
}
/// <https://fetch.spec.whatwg.org/#dom-global-fetch>
#[allow(non_snake_case)]
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
@ -151,6 +176,7 @@ pub(crate) fn Fetch(
) -> Rc<Promise> {
// Step 1. Let p be a new promise.
let promise = Promise::new_in_current_realm(comp, can_gc);
let cx = GlobalScope::get_cx();
// Step 7. Let responseObject be null.
// NOTE: We do initialize the object earlier earlier so we can use it to track errors
@ -159,32 +185,41 @@ pub(crate) fn Fetch(
// Step 2. Let requestObject be the result of invoking the initial value of Request as constructor
// with input and init as arguments. If this throws an exception, reject p with it and return p.
let request = match Request::Constructor(global, None, can_gc, input, init) {
let request_object = match Request::Constructor(global, None, can_gc, input, init) {
Err(e) => {
response.error_stream(e.clone(), can_gc);
promise.reject_error(e, can_gc);
return promise;
},
Ok(r) => {
// Step 3. Let request be requestObjects request.
r.get_request()
},
Ok(r) => r,
};
// Step 3. Let request be requestObjects request.
let request = request_object.get_request();
let timing_type = request.timing_type();
let mut request_init = request_init_from_request(request);
request_init.policy_container =
RequestPolicyContainer::PolicyContainer(global.policy_container());
// Step 4. If requestObjects signal is aborted, then:
// TODO
// Step 4.1. Abort the fetch() call with p, request, null, and requestObjects signals abort reason.
// TODO
// Step 4.2. Return p.
// TODO
let signal = request_object.Signal();
if signal.aborted() {
// Step 4.1. Abort the fetch() call with p, request, null, and requestObjects signals abort reason.
rooted!(in(*cx) let mut abort_reason = UndefinedValue());
signal.Reason(cx, abort_reason.handle_mut());
abort_fetch_call(
promise.clone(),
&request,
None,
abort_reason.handle(),
cx,
can_gc,
);
// Step 4.2. Return p.
return promise;
}
// Step 5. Let globalObject be requests clients global object.
// NOTE: We already get the global object as an argument
let mut request_init = request_init_from_request(request);
request_init.policy_container =
RequestPolicyContainer::PolicyContainer(global.policy_container());
// Step 6. If globalObject is a ServiceWorkerGlobalScope object, then set requests
// service-workers mode to "none".

View file

@ -6,30 +6,6 @@
[general.any.html]
expected: TIMEOUT
[Aborting rejects with AbortError]
expected: FAIL
[Aborting rejects with AbortError - no-cors]
expected: FAIL
[Signal on request object]
expected: FAIL
[Signal on request object created from request object]
expected: FAIL
[Signal on request object created from request object, with signal on second request]
expected: FAIL
[Signal on request object created from request object, with signal on second request overriding another]
expected: FAIL
[Signal retained after unrelated properties are overridden by fetch]
expected: FAIL
[Already aborted signal rejects immediately]
expected: FAIL
[Request is still 'used' if signal is aborted before fetching]
expected: FAIL
@ -51,15 +27,6 @@
[Call text() twice on aborted response]
expected: FAIL
[Already aborted signal does not make request]
expected: FAIL
[Already aborted signal can be used for many fetches]
expected: FAIL
[Signal can be used to abort other fetches, even if another fetch succeeded before aborting]
expected: FAIL
[Underlying connection is closed when aborting after receiving response]
expected: FAIL
@ -93,12 +60,6 @@
[Readable stream synchronously cancels with AbortError if aborted before reading]
expected: NOTRUN
[Aborting rejects with abort reason]
expected: FAIL
[Signal on request object should also have abort reason]
expected: FAIL
[response.bytes() rejects if already aborted]
expected: FAIL
@ -108,30 +69,6 @@
[general.any.worker.html]
expected: TIMEOUT
[Aborting rejects with AbortError]
expected: FAIL
[Aborting rejects with AbortError - no-cors]
expected: FAIL
[Signal on request object]
expected: FAIL
[Signal on request object created from request object]
expected: FAIL
[Signal on request object created from request object, with signal on second request]
expected: FAIL
[Signal on request object created from request object, with signal on second request overriding another]
expected: FAIL
[Signal retained after unrelated properties are overridden by fetch]
expected: FAIL
[Already aborted signal rejects immediately]
expected: FAIL
[Request is still 'used' if signal is aborted before fetching]
expected: FAIL
@ -153,15 +90,6 @@
[Call text() twice on aborted response]
expected: FAIL
[Already aborted signal does not make request]
expected: FAIL
[Already aborted signal can be used for many fetches]
expected: FAIL
[Signal can be used to abort other fetches, even if another fetch succeeded before aborting]
expected: FAIL
[Underlying connection is closed when aborting after receiving response]
expected: FAIL
@ -195,12 +123,6 @@
[Readable stream synchronously cancels with AbortError if aborted before reading]
expected: NOTRUN
[Aborting rejects with abort reason]
expected: FAIL
[Signal on request object should also have abort reason]
expected: FAIL
[response.bytes() rejects if already aborted]
expected: FAIL