script: Implement the Bytes() method on Request and Response (#35250)

* Implement the Bytes() method on Request and Response

Signed-off-by: Shane Handley <shanehandley@fastmail.com>

* avoid unsafe code during buffer creation

Signed-off-by: Shane Handley <shanehandley@fastmail.com>

---------

Signed-off-by: Shane Handley <shanehandley@fastmail.com>
This commit is contained in:
shanehandley 2025-02-02 18:49:48 +11:00 committed by GitHub
parent f364b3f6ea
commit 938baf6bf3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 33 additions and 149 deletions

View file

@ -12,7 +12,7 @@ use js::jsapi::{Heap, JSObject, JS_ClearPendingException, Value as JSValue};
use js::jsval::{JSVal, UndefinedValue};
use js::rust::wrappers::{JS_GetPendingException, JS_ParseJSON};
use js::rust::HandleValue;
use js::typedarray::{ArrayBuffer, CreateWith};
use js::typedarray::{ArrayBufferU8, Uint8};
use mime::{self, Mime};
use net_traits::request::{
BodyChunkRequest, BodyChunkResponse, BodySource as NetBodySource, RequestBody,
@ -20,6 +20,7 @@ use net_traits::request::{
use script_traits::serializable::BlobImpl;
use url::form_urlencoded;
use crate::dom::bindings::buffer_source::create_buffer_source;
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::BlobBinding::Blob_Binding::BlobMethods;
use crate::dom::bindings::codegen::Bindings::FormDataBinding::FormDataMethods;
@ -572,6 +573,7 @@ impl Extractable for URLSearchParams {
#[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
pub(crate) enum BodyType {
Blob,
Bytes,
FormData,
Json,
Text,
@ -582,6 +584,7 @@ pub(crate) enum FetchedData {
Text(String),
Json(RootedTraceableBox<Heap<JSValue>>),
BlobData(DomRoot<Blob>),
Bytes(RootedTraceableBox<Heap<*mut JSObject>>),
FormData(DomRoot<FormData>),
ArrayBuffer(RootedTraceableBox<Heap<*mut JSObject>>),
JSException(RootedTraceableBox<Heap<JSVal>>),
@ -633,6 +636,7 @@ impl ConsumeBodyPromiseHandler {
FetchedData::Json(j) => self.result_promise.resolve_native(&j),
FetchedData::BlobData(b) => self.result_promise.resolve_native(&b),
FetchedData::FormData(f) => self.result_promise.resolve_native(&f),
FetchedData::Bytes(b) => self.result_promise.resolve_native(&b),
FetchedData::ArrayBuffer(a) => self.result_promise.resolve_native(&a),
FetchedData::JSException(e) => self.result_promise.reject_native(&e.handle()),
};
@ -810,6 +814,7 @@ fn run_package_data_algorithm(
BodyType::Blob => run_blob_data_algorithm(&global, bytes, mime, can_gc),
BodyType::FormData => run_form_data_algorithm(&global, bytes, mime, can_gc),
BodyType::ArrayBuffer => run_array_buffer_data_algorithm(cx, bytes),
BodyType::Bytes => run_bytes_data_algorithm(cx, bytes),
}
}
@ -891,22 +896,25 @@ fn run_form_data_algorithm(
Err(Error::Type("Inappropriate MIME-type for Body".to_string()))
}
#[allow(unsafe_code)]
fn run_bytes_data_algorithm(cx: JSContext, bytes: Vec<u8>) -> Fallible<FetchedData> {
rooted!(in(*cx) let mut array_buffer_ptr = ptr::null_mut::<JSObject>());
create_buffer_source::<Uint8>(cx, &bytes, array_buffer_ptr.handle_mut())
.map_err(|_| Error::JSFailed)?;
let rooted_heap = RootedTraceableBox::from_box(Heap::boxed(array_buffer_ptr.get()));
Ok(FetchedData::Bytes(rooted_heap))
}
pub(crate) fn run_array_buffer_data_algorithm(
cx: JSContext,
bytes: Vec<u8>,
) -> Fallible<FetchedData> {
rooted!(in(*cx) let mut array_buffer_ptr = ptr::null_mut::<JSObject>());
let arraybuffer = unsafe {
ArrayBuffer::create(
*cx,
CreateWith::Slice(&bytes),
array_buffer_ptr.handle_mut(),
)
};
if arraybuffer.is_err() {
return Err(Error::JSFailed);
}
create_buffer_source::<ArrayBufferU8>(cx, &bytes, array_buffer_ptr.handle_mut())
.map_err(|_| Error::JSFailed)?;
let rooted_heap = RootedTraceableBox::from_box(Heap::boxed(array_buffer_ptr.get()));
Ok(FetchedData::ArrayBuffer(rooted_heap))
}

View file

@ -643,6 +643,11 @@ impl RequestMethods<crate::DomTypeHolder> for Request {
fn ArrayBuffer(&self, can_gc: CanGc) -> Rc<Promise> {
consume_body(self, BodyType::ArrayBuffer, can_gc)
}
/// <https://fetch.spec.whatwg.org/#dom-body-bytes>
fn Bytes(&self, can_gc: CanGc) -> std::rc::Rc<Promise> {
consume_body(self, BodyType::Bytes, can_gc)
}
}
impl BodyMixin for Request {

View file

@ -386,6 +386,11 @@ impl ResponseMethods<crate::DomTypeHolder> for Response {
fn ArrayBuffer(&self, can_gc: CanGc) -> Rc<Promise> {
consume_body(self, BodyType::ArrayBuffer, can_gc)
}
/// <https://fetch.spec.whatwg.org/#dom-body-bytes>
fn Bytes(&self, can_gc: CanGc) -> std::rc::Rc<Promise> {
consume_body(self, BodyType::Bytes, can_gc)
}
}
fn serialize_without_fragment(url: &ServoUrl) -> &str {

View file

@ -414,11 +414,11 @@ DOMInterfaces = {
},
'Request': {
'canGc': ['Headers', 'Text', 'Blob', 'FormData', 'Json', 'ArrayBuffer', 'Clone'],
'canGc': ['Headers', 'Text', 'Blob', 'FormData', 'Json', 'ArrayBuffer', 'Clone', 'Bytes'],
},
'Response': {
'canGc': ['Error', 'Redirect', 'Clone', 'Text', 'Blob', 'FormData', 'Json', 'ArrayBuffer', 'Headers'],
'canGc': ['Error', 'Redirect', 'Clone', 'Text', 'Blob', 'FormData', 'Json', 'ArrayBuffer', 'Headers', 'Bytes'],
},
'RTCPeerConnection': {

View file

@ -11,6 +11,7 @@ interface mixin Body {
[NewObject] Promise<ArrayBuffer> arrayBuffer();
[NewObject] Promise<Blob> blob();
[NewObject] Promise<Uint8Array> bytes();
[NewObject] Promise<FormData> formData();
[NewObject] Promise<any> json();
[NewObject] Promise<USVString> text();

View file

@ -14,16 +14,6 @@
[Request interface: attribute duplex]
expected: FAIL
[Request interface: operation arrayBuffer()]
[Request interface: operation blob()]
[Request interface: operation formData()]
[Request interface: operation json()]
[Request interface: operation text()]
[Request interface: new Request('about:blank') must inherit property "keepalive" with the proper type]
expected: FAIL
@ -42,35 +32,9 @@
[Response interface: operation json(any, optional ResponseInit)]
expected: FAIL
[Response interface: operation arrayBuffer()]
[Response interface: operation blob()]
[Response interface: operation formData()]
[Response interface: operation json()]
[Response interface: operation text()]
[Response interface: calling json(any, optional ResponseInit) on new Response() with too few arguments must throw TypeError]
expected: FAIL
[WorkerGlobalScope interface: operation fetch(RequestInfo, optional RequestInit)]
[WorkerGlobalScope interface: calling fetch(RequestInfo, optional RequestInit) on self with too few arguments must throw TypeError]
[Request interface: operation bytes()]
expected: FAIL
[Request interface: new Request('about:blank') must inherit property "bytes()" with the proper type]
expected: FAIL
[Response interface: operation bytes()]
expected: FAIL
[Response interface: new Response() must inherit property "bytes()" with the proper type]
expected: FAIL
[idlharness.any.sharedworker.html]
expected: ERROR
@ -91,16 +55,6 @@
[Request interface: attribute duplex]
expected: FAIL
[Request interface: operation arrayBuffer()]
[Request interface: operation blob()]
[Request interface: operation formData()]
[Request interface: operation json()]
[Request interface: operation text()]
[Request interface: new Request('about:blank') must inherit property "keepalive" with the proper type]
expected: FAIL
@ -119,35 +73,9 @@
[Response interface: operation json(any, optional ResponseInit)]
expected: FAIL
[Response interface: operation arrayBuffer()]
[Response interface: operation blob()]
[Response interface: operation formData()]
[Response interface: operation json()]
[Response interface: operation text()]
[Response interface: calling json(any, optional ResponseInit) on new Response() with too few arguments must throw TypeError]
expected: FAIL
[Window interface: operation fetch(RequestInfo, optional RequestInit)]
[Window interface: calling fetch(RequestInfo, optional RequestInit) on window with too few arguments must throw TypeError]
[Request interface: operation bytes()]
expected: FAIL
[Request interface: new Request('about:blank') must inherit property "bytes()" with the proper type]
expected: FAIL
[Response interface: operation bytes()]
expected: FAIL
[Response interface: new Response() must inherit property "bytes()" with the proper type]
expected: FAIL
[idlharness.any.serviceworker.html]
expected: ERROR

View file

@ -2,53 +2,11 @@
[Consume FormData request's body as FormData]
expected: FAIL
[Consume String request's body as bytes]
expected: FAIL
[Consume ArrayBuffer request's body as bytes]
expected: FAIL
[Consume Uint8Array request's body as bytes]
expected: FAIL
[Consume Int8Array request's body as bytes]
expected: FAIL
[Consume Float32Array request's body as bytes]
expected: FAIL
[Consume DataView request's body as bytes]
expected: FAIL
[Consume blob response's body as bytes]
expected: FAIL
[request-consume.any.worker.html]
[Consume FormData request's body as FormData]
expected: FAIL
[Consume String request's body as bytes]
expected: FAIL
[Consume ArrayBuffer request's body as bytes]
expected: FAIL
[Consume Uint8Array request's body as bytes]
expected: FAIL
[Consume Int8Array request's body as bytes]
expected: FAIL
[Consume Float32Array request's body as bytes]
expected: FAIL
[Consume DataView request's body as bytes]
expected: FAIL
[Consume blob response's body as bytes]
expected: FAIL
[request-consume.any.serviceworker.html]
expected: ERROR

View file

@ -1,7 +1,4 @@
[response-blob-realm.any.html]
[realm of the Uint8Array from Response bytes()]
expected: FAIL
[response-blob-realm.any.worker.html]
[realm of the Uint8Array from Response bytes()]

View file

@ -1,21 +1,9 @@
[response-error-from-stream.any.worker.html]
[ReadableStream start() Error propagates to Response.bytes() Promise]
expected: FAIL
[ReadableStream pull() Error propagates to Response.bytes() Promise]
expected: FAIL
[response-error-from-stream.any.serviceworker.html]
expected: ERROR
[response-error-from-stream.any.html]
[ReadableStream start() Error propagates to Response.bytes() Promise]
expected: FAIL
[ReadableStream pull() Error propagates to Response.bytes() Promise]
expected: FAIL
[response-error-from-stream.any.sharedworker.html]
expected: ERROR

View file

@ -2,14 +2,8 @@
expected: ERROR
[response-stream-bad-chunk.any.html]
[ReadableStream with non-Uint8Array chunk passed to Response.bytes() causes TypeError]
expected: FAIL
[response-stream-bad-chunk.any.worker.html]
[ReadableStream with non-Uint8Array chunk passed to Response.bytes() causes TypeError]
expected: FAIL
[response-stream-bad-chunk.any.serviceworker.html]
expected: ERROR