mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
Propagate CanGc when interacting with readable streams. (#33975)
Signed-off-by: Josh Matthews <josh@joshmatthews.net>
This commit is contained in:
parent
f553bda7eb
commit
12e6ec25aa
10 changed files with 80 additions and 57 deletions
|
@ -438,21 +438,21 @@ impl ExtractedBody {
|
|||
|
||||
/// <https://fetch.spec.whatwg.org/#concept-bodyinit-extract>
|
||||
pub trait Extractable {
|
||||
fn extract(&self, global: &GlobalScope) -> Fallible<ExtractedBody>;
|
||||
fn extract(&self, global: &GlobalScope, can_gc: CanGc) -> Fallible<ExtractedBody>;
|
||||
}
|
||||
|
||||
impl Extractable for BodyInit {
|
||||
// https://fetch.spec.whatwg.org/#concept-bodyinit-extract
|
||||
fn extract(&self, global: &GlobalScope) -> Fallible<ExtractedBody> {
|
||||
fn extract(&self, global: &GlobalScope, can_gc: CanGc) -> Fallible<ExtractedBody> {
|
||||
match self {
|
||||
BodyInit::String(ref s) => s.extract(global),
|
||||
BodyInit::URLSearchParams(ref usp) => usp.extract(global),
|
||||
BodyInit::Blob(ref b) => b.extract(global),
|
||||
BodyInit::FormData(ref formdata) => formdata.extract(global),
|
||||
BodyInit::String(ref s) => s.extract(global, can_gc),
|
||||
BodyInit::URLSearchParams(ref usp) => usp.extract(global, can_gc),
|
||||
BodyInit::Blob(ref b) => b.extract(global, can_gc),
|
||||
BodyInit::FormData(ref formdata) => formdata.extract(global, can_gc),
|
||||
BodyInit::ArrayBuffer(ref typedarray) => {
|
||||
let bytes = typedarray.to_vec();
|
||||
let total_bytes = bytes.len();
|
||||
let stream = ReadableStream::new_from_bytes(global, bytes);
|
||||
let stream = ReadableStream::new_from_bytes(global, bytes, can_gc);
|
||||
Ok(ExtractedBody {
|
||||
stream,
|
||||
total_bytes: Some(total_bytes),
|
||||
|
@ -463,7 +463,7 @@ impl Extractable for BodyInit {
|
|||
BodyInit::ArrayBufferView(ref typedarray) => {
|
||||
let bytes = typedarray.to_vec();
|
||||
let total_bytes = bytes.len();
|
||||
let stream = ReadableStream::new_from_bytes(global, bytes);
|
||||
let stream = ReadableStream::new_from_bytes(global, bytes, can_gc);
|
||||
Ok(ExtractedBody {
|
||||
stream,
|
||||
total_bytes: Some(total_bytes),
|
||||
|
@ -493,10 +493,10 @@ impl Extractable for BodyInit {
|
|||
}
|
||||
|
||||
impl Extractable for Vec<u8> {
|
||||
fn extract(&self, global: &GlobalScope) -> Fallible<ExtractedBody> {
|
||||
fn extract(&self, global: &GlobalScope, can_gc: CanGc) -> Fallible<ExtractedBody> {
|
||||
let bytes = self.clone();
|
||||
let total_bytes = self.len();
|
||||
let stream = ReadableStream::new_from_bytes(global, bytes);
|
||||
let stream = ReadableStream::new_from_bytes(global, bytes, can_gc);
|
||||
Ok(ExtractedBody {
|
||||
stream,
|
||||
total_bytes: Some(total_bytes),
|
||||
|
@ -508,7 +508,7 @@ impl Extractable for Vec<u8> {
|
|||
}
|
||||
|
||||
impl Extractable for Blob {
|
||||
fn extract(&self, _global: &GlobalScope) -> Fallible<ExtractedBody> {
|
||||
fn extract(&self, _global: &GlobalScope, can_gc: CanGc) -> Fallible<ExtractedBody> {
|
||||
let blob_type = self.Type();
|
||||
let content_type = if blob_type.as_ref().is_empty() {
|
||||
None
|
||||
|
@ -517,7 +517,7 @@ impl Extractable for Blob {
|
|||
};
|
||||
let total_bytes = self.Size() as usize;
|
||||
Ok(ExtractedBody {
|
||||
stream: self.get_stream(),
|
||||
stream: self.get_stream(can_gc),
|
||||
total_bytes: Some(total_bytes),
|
||||
content_type,
|
||||
source: BodySource::Object,
|
||||
|
@ -526,11 +526,11 @@ impl Extractable for Blob {
|
|||
}
|
||||
|
||||
impl Extractable for DOMString {
|
||||
fn extract(&self, global: &GlobalScope) -> Fallible<ExtractedBody> {
|
||||
fn extract(&self, global: &GlobalScope, can_gc: CanGc) -> Fallible<ExtractedBody> {
|
||||
let bytes = self.as_bytes().to_owned();
|
||||
let total_bytes = bytes.len();
|
||||
let content_type = Some(DOMString::from("text/plain;charset=UTF-8"));
|
||||
let stream = ReadableStream::new_from_bytes(global, bytes);
|
||||
let stream = ReadableStream::new_from_bytes(global, bytes, can_gc);
|
||||
Ok(ExtractedBody {
|
||||
stream,
|
||||
total_bytes: Some(total_bytes),
|
||||
|
@ -541,7 +541,7 @@ impl Extractable for DOMString {
|
|||
}
|
||||
|
||||
impl Extractable for FormData {
|
||||
fn extract(&self, global: &GlobalScope) -> Fallible<ExtractedBody> {
|
||||
fn extract(&self, global: &GlobalScope, can_gc: CanGc) -> Fallible<ExtractedBody> {
|
||||
let boundary = generate_boundary();
|
||||
let bytes = encode_multipart_form_data(&mut self.datums(), boundary.clone(), UTF_8);
|
||||
let total_bytes = bytes.len();
|
||||
|
@ -549,7 +549,7 @@ impl Extractable for FormData {
|
|||
"multipart/form-data;boundary={}",
|
||||
boundary
|
||||
)));
|
||||
let stream = ReadableStream::new_from_bytes(global, bytes);
|
||||
let stream = ReadableStream::new_from_bytes(global, bytes, can_gc);
|
||||
Ok(ExtractedBody {
|
||||
stream,
|
||||
total_bytes: Some(total_bytes),
|
||||
|
@ -560,13 +560,13 @@ impl Extractable for FormData {
|
|||
}
|
||||
|
||||
impl Extractable for URLSearchParams {
|
||||
fn extract(&self, global: &GlobalScope) -> Fallible<ExtractedBody> {
|
||||
fn extract(&self, global: &GlobalScope, can_gc: CanGc) -> Fallible<ExtractedBody> {
|
||||
let bytes = self.serialize_utf8().into_bytes();
|
||||
let total_bytes = bytes.len();
|
||||
let content_type = Some(DOMString::from(
|
||||
"application/x-www-form-urlencoded;charset=UTF-8",
|
||||
));
|
||||
let stream = ReadableStream::new_from_bytes(global, bytes);
|
||||
let stream = ReadableStream::new_from_bytes(global, bytes, can_gc);
|
||||
Ok(ExtractedBody {
|
||||
stream,
|
||||
total_bytes: Some(total_bytes),
|
||||
|
@ -756,7 +756,7 @@ fn consume_body_with_promise<T: BodyMixin + DomObject>(
|
|||
// Step 2.
|
||||
let stream = match object.body() {
|
||||
Some(stream) => stream,
|
||||
None => ReadableStream::new_from_bytes(&global, Vec::with_capacity(0)),
|
||||
None => ReadableStream::new_from_bytes(&global, Vec::with_capacity(0), can_gc),
|
||||
};
|
||||
|
||||
// Step 3.
|
||||
|
|
|
@ -30,7 +30,7 @@ DOMInterfaces = {
|
|||
|
||||
'Blob': {
|
||||
'weakReferenceable': True,
|
||||
'canGc': ['Slice', 'Text', 'ArrayBuffer'],
|
||||
'canGc': ['Slice', 'Text', 'ArrayBuffer', 'Stream'],
|
||||
},
|
||||
|
||||
'Bluetooth': {
|
||||
|
|
|
@ -86,8 +86,8 @@ impl Blob {
|
|||
}
|
||||
|
||||
/// <https://w3c.github.io/FileAPI/#blob-get-stream>
|
||||
pub fn get_stream(&self) -> DomRoot<ReadableStream> {
|
||||
self.global().get_blob_stream(&self.blob_id)
|
||||
pub fn get_stream(&self, can_gc: CanGc) -> DomRoot<ReadableStream> {
|
||||
self.global().get_blob_stream(&self.blob_id, can_gc)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -237,8 +237,8 @@ impl BlobMethods for Blob {
|
|||
}
|
||||
|
||||
// <https://w3c.github.io/FileAPI/#blob-get-stream>
|
||||
fn Stream(&self, _cx: JSContext) -> NonNull<JSObject> {
|
||||
self.get_stream().get_js_stream()
|
||||
fn Stream(&self, _cx: JSContext, can_gc: CanGc) -> NonNull<JSObject> {
|
||||
self.get_stream(can_gc).get_js_stream()
|
||||
}
|
||||
|
||||
// https://w3c.github.io/FileAPI/#slice-method-algo
|
||||
|
|
|
@ -617,10 +617,10 @@ impl MessageListener {
|
|||
}
|
||||
|
||||
/// Callback used to enqueue file chunks to streams as part of FileListener.
|
||||
fn stream_handle_incoming(stream: &ReadableStream, bytes: Fallible<Vec<u8>>) {
|
||||
fn stream_handle_incoming(stream: &ReadableStream, bytes: Fallible<Vec<u8>>, can_gc: CanGc) {
|
||||
match bytes {
|
||||
Ok(b) => {
|
||||
stream.enqueue_native(b);
|
||||
stream.enqueue_native(b, can_gc);
|
||||
},
|
||||
Err(e) => {
|
||||
stream.error_native(e);
|
||||
|
@ -643,7 +643,7 @@ impl FileListener {
|
|||
|
||||
let task = task!(enqueue_stream_chunk: move || {
|
||||
let stream = trusted.root();
|
||||
stream_handle_incoming(&stream, Ok(blob_buf.bytes));
|
||||
stream_handle_incoming(&stream, Ok(blob_buf.bytes), CanGc::note());
|
||||
});
|
||||
|
||||
let _ = self
|
||||
|
@ -667,7 +667,7 @@ impl FileListener {
|
|||
|
||||
let task = task!(enqueue_stream_chunk: move || {
|
||||
let stream = trusted.root();
|
||||
stream_handle_incoming(&stream, Ok(bytes_in));
|
||||
stream_handle_incoming(&stream, Ok(bytes_in), CanGc::note());
|
||||
});
|
||||
|
||||
let _ = self
|
||||
|
@ -733,7 +733,7 @@ impl FileListener {
|
|||
let _ = self.task_source.queue_with_canceller(
|
||||
task!(error_stream: move || {
|
||||
let stream = trusted_stream.root();
|
||||
stream_handle_incoming(&stream, error);
|
||||
stream_handle_incoming(&stream, error, CanGc::note());
|
||||
}),
|
||||
&self.task_canceller,
|
||||
);
|
||||
|
@ -1988,11 +1988,11 @@ impl GlobalScope {
|
|||
}
|
||||
|
||||
/// <https://w3c.github.io/FileAPI/#blob-get-stream>
|
||||
pub fn get_blob_stream(&self, blob_id: &BlobId) -> DomRoot<ReadableStream> {
|
||||
pub fn get_blob_stream(&self, blob_id: &BlobId, can_gc: CanGc) -> DomRoot<ReadableStream> {
|
||||
let (file_id, size) = match self.get_blob_bytes_or_file_id(blob_id) {
|
||||
BlobResult::Bytes(bytes) => {
|
||||
// If we have all the bytes in memory, queue them and close the stream.
|
||||
let stream = ReadableStream::new_from_bytes(self, bytes);
|
||||
let stream = ReadableStream::new_from_bytes(self, bytes, can_gc);
|
||||
return stream;
|
||||
},
|
||||
BlobResult::File(id, size) => (id, size),
|
||||
|
|
|
@ -870,6 +870,7 @@ impl HTMLFormElement {
|
|||
enctype,
|
||||
encoding,
|
||||
target_window,
|
||||
can_gc,
|
||||
);
|
||||
},
|
||||
// https://html.spec.whatwg.org/multipage/#submit-get-action
|
||||
|
@ -920,6 +921,7 @@ impl HTMLFormElement {
|
|||
enctype: FormEncType,
|
||||
encoding: &'static Encoding,
|
||||
target: &Window,
|
||||
can_gc: CanGc,
|
||||
) {
|
||||
let boundary = generate_boundary();
|
||||
let bytes = match enctype {
|
||||
|
@ -957,7 +959,7 @@ impl HTMLFormElement {
|
|||
let global = self.global();
|
||||
|
||||
let request_body = bytes
|
||||
.extract(&global)
|
||||
.extract(&global, can_gc)
|
||||
.expect("Couldn't extract body.")
|
||||
.into_net_request_body()
|
||||
.0;
|
||||
|
|
|
@ -34,7 +34,7 @@ use crate::dom::globalscope::GlobalScope;
|
|||
use crate::dom::promise::Promise;
|
||||
use crate::js::conversions::FromJSValConvertible;
|
||||
use crate::realms::{enter_realm, InRealm};
|
||||
use crate::script_runtime::JSContext as SafeJSContext;
|
||||
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
|
||||
|
||||
static UNDERLYING_SOURCE_TRAPS: ReadableStreamUnderlyingSourceTraps =
|
||||
ReadableStreamUnderlyingSourceTraps {
|
||||
|
@ -101,12 +101,16 @@ impl ReadableStream {
|
|||
}
|
||||
|
||||
/// Build a stream backed by a Rust source that has already been read into memory.
|
||||
pub fn new_from_bytes(global: &GlobalScope, bytes: Vec<u8>) -> DomRoot<ReadableStream> {
|
||||
pub fn new_from_bytes(
|
||||
global: &GlobalScope,
|
||||
bytes: Vec<u8>,
|
||||
can_gc: CanGc,
|
||||
) -> DomRoot<ReadableStream> {
|
||||
let stream = ReadableStream::new_with_external_underlying_source(
|
||||
global,
|
||||
ExternalUnderlyingSource::Memory(bytes.len()),
|
||||
);
|
||||
stream.enqueue_native(bytes);
|
||||
stream.enqueue_native(bytes, can_gc);
|
||||
stream.close_native();
|
||||
stream
|
||||
}
|
||||
|
@ -153,7 +157,7 @@ impl ReadableStream {
|
|||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
pub fn enqueue_native(&self, bytes: Vec<u8>) {
|
||||
pub fn enqueue_native(&self, bytes: Vec<u8>, can_gc: CanGc) {
|
||||
let global = self.global();
|
||||
let _ar = enter_realm(&*global);
|
||||
let cx = GlobalScope::get_cx();
|
||||
|
@ -163,7 +167,7 @@ impl ReadableStream {
|
|||
self.external_underlying_source
|
||||
.as_ref()
|
||||
.expect("No external source to enqueue bytes.")
|
||||
.enqueue_chunk(cx, handle, bytes);
|
||||
.enqueue_chunk(cx, handle, bytes, can_gc);
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
|
@ -324,7 +328,12 @@ unsafe extern "C" fn request_data(
|
|||
desired_size: usize,
|
||||
) {
|
||||
let source = &*(source as *const ExternalUnderlyingSourceController);
|
||||
source.pull(SafeJSContext::from_ptr(cx), stream, desired_size);
|
||||
source.pull(
|
||||
SafeJSContext::from_ptr(cx),
|
||||
stream,
|
||||
desired_size,
|
||||
CanGc::note(),
|
||||
);
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
|
@ -427,12 +436,15 @@ impl ExternalUnderlyingSourceController {
|
|||
}
|
||||
|
||||
/// Signal available bytes if the stream is currently readable.
|
||||
/// The apparently unused CanGc argument represents that the JS API calls like
|
||||
/// `ReadableStreamUpdateDataAvailableFromSource` can trigger a GC.
|
||||
#[allow(unsafe_code)]
|
||||
fn maybe_signal_available_bytes(
|
||||
&self,
|
||||
cx: SafeJSContext,
|
||||
stream: HandleObject,
|
||||
available: usize,
|
||||
_can_gc: CanGc,
|
||||
) {
|
||||
if available == 0 {
|
||||
return;
|
||||
|
@ -467,17 +479,23 @@ impl ExternalUnderlyingSourceController {
|
|||
self.maybe_close_js_stream(cx, stream);
|
||||
}
|
||||
|
||||
fn enqueue_chunk(&self, cx: SafeJSContext, stream: HandleObject, mut chunk: Vec<u8>) {
|
||||
fn enqueue_chunk(
|
||||
&self,
|
||||
cx: SafeJSContext,
|
||||
stream: HandleObject,
|
||||
mut chunk: Vec<u8>,
|
||||
can_gc: CanGc,
|
||||
) {
|
||||
let available = {
|
||||
let mut buffer = self.buffer.borrow_mut();
|
||||
buffer.append(&mut chunk);
|
||||
buffer.len()
|
||||
};
|
||||
self.maybe_signal_available_bytes(cx, stream, available);
|
||||
self.maybe_signal_available_bytes(cx, stream, available, can_gc);
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
fn pull(&self, cx: SafeJSContext, stream: HandleObject, _desired_size: usize) {
|
||||
fn pull(&self, cx: SafeJSContext, stream: HandleObject, _desired_size: usize, can_gc: CanGc) {
|
||||
// Note: for pull sources,
|
||||
// this would be the time to ask for a chunk.
|
||||
|
||||
|
@ -490,7 +508,7 @@ impl ExternalUnderlyingSourceController {
|
|||
buffer.len()
|
||||
};
|
||||
|
||||
self.maybe_signal_available_bytes(cx, stream, available);
|
||||
self.maybe_signal_available_bytes(cx, stream, available, can_gc);
|
||||
}
|
||||
|
||||
fn get_chunk_with_length(&self, length: usize) -> Vec<u8> {
|
||||
|
|
|
@ -477,7 +477,7 @@ impl RequestMethods for Request {
|
|||
// Step 37.1 TODO "If init["keepalive"] exists and is true..."
|
||||
|
||||
// Step 37.2
|
||||
let mut extracted_body = init_body.extract(global)?;
|
||||
let mut extracted_body = init_body.extract(global, can_gc)?;
|
||||
|
||||
// Step 37.3
|
||||
if let Some(contents) = extracted_body.content_type.take() {
|
||||
|
|
|
@ -190,7 +190,7 @@ impl ResponseMethods for Response {
|
|||
total_bytes: _,
|
||||
content_type,
|
||||
source: _,
|
||||
} = body.extract(global)?;
|
||||
} = body.extract(global, can_gc)?;
|
||||
|
||||
r.body_stream.set(Some(&*stream));
|
||||
|
||||
|
@ -210,7 +210,7 @@ impl ResponseMethods for Response {
|
|||
} else {
|
||||
// Reset FetchResponse to an in-memory stream with empty byte sequence here for
|
||||
// no-init-body case
|
||||
let stream = ReadableStream::new_from_bytes(global, Vec::with_capacity(0));
|
||||
let stream = ReadableStream::new_from_bytes(global, Vec::with_capacity(0), can_gc);
|
||||
r.body_stream.set(Some(&*stream));
|
||||
}
|
||||
|
||||
|
@ -444,12 +444,12 @@ impl Response {
|
|||
*self.stream_consumer.borrow_mut() = sc;
|
||||
}
|
||||
|
||||
pub fn stream_chunk(&self, chunk: Vec<u8>) {
|
||||
pub fn stream_chunk(&self, chunk: Vec<u8>, can_gc: CanGc) {
|
||||
// Note, are these two actually mutually exclusive?
|
||||
if let Some(stream_consumer) = self.stream_consumer.borrow_mut().as_ref() {
|
||||
stream_consumer.consume_chunk(chunk.as_slice());
|
||||
} else if let Some(body) = self.body_stream.get() {
|
||||
body.enqueue_native(chunk);
|
||||
body.enqueue_native(chunk, can_gc);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -576,7 +576,7 @@ impl XMLHttpRequestMethods for XMLHttpRequest {
|
|||
};
|
||||
let total_bytes = bytes.len();
|
||||
let global = self.global();
|
||||
let stream = ReadableStream::new_from_bytes(&global, bytes);
|
||||
let stream = ReadableStream::new_from_bytes(&global, bytes, can_gc);
|
||||
Some(ExtractedBody {
|
||||
stream,
|
||||
total_bytes: Some(total_bytes),
|
||||
|
@ -585,7 +585,9 @@ impl XMLHttpRequestMethods for XMLHttpRequest {
|
|||
})
|
||||
},
|
||||
Some(DocumentOrXMLHttpRequestBodyInit::Blob(ref b)) => {
|
||||
let extracted_body = b.extract(&self.global()).expect("Couldn't extract body.");
|
||||
let extracted_body = b
|
||||
.extract(&self.global(), can_gc)
|
||||
.expect("Couldn't extract body.");
|
||||
if !extracted_body.in_memory() && self.sync.get() {
|
||||
warn!("Sync XHR with not in-memory Blob as body not supported");
|
||||
None
|
||||
|
@ -595,22 +597,23 @@ impl XMLHttpRequestMethods for XMLHttpRequest {
|
|||
},
|
||||
Some(DocumentOrXMLHttpRequestBodyInit::FormData(ref formdata)) => Some(
|
||||
formdata
|
||||
.extract(&self.global())
|
||||
.extract(&self.global(), can_gc)
|
||||
.expect("Couldn't extract body."),
|
||||
),
|
||||
Some(DocumentOrXMLHttpRequestBodyInit::String(ref str)) => Some(
|
||||
str.extract(&self.global(), can_gc)
|
||||
.expect("Couldn't extract body."),
|
||||
),
|
||||
Some(DocumentOrXMLHttpRequestBodyInit::String(ref str)) => {
|
||||
Some(str.extract(&self.global()).expect("Couldn't extract body."))
|
||||
},
|
||||
Some(DocumentOrXMLHttpRequestBodyInit::URLSearchParams(ref urlsp)) => Some(
|
||||
urlsp
|
||||
.extract(&self.global())
|
||||
.extract(&self.global(), can_gc)
|
||||
.expect("Couldn't extract body."),
|
||||
),
|
||||
Some(DocumentOrXMLHttpRequestBodyInit::ArrayBuffer(ref typedarray)) => {
|
||||
let bytes = typedarray.to_vec();
|
||||
let total_bytes = bytes.len();
|
||||
let global = self.global();
|
||||
let stream = ReadableStream::new_from_bytes(&global, bytes);
|
||||
let stream = ReadableStream::new_from_bytes(&global, bytes, can_gc);
|
||||
Some(ExtractedBody {
|
||||
stream,
|
||||
total_bytes: Some(total_bytes),
|
||||
|
@ -622,7 +625,7 @@ impl XMLHttpRequestMethods for XMLHttpRequest {
|
|||
let bytes = typedarray.to_vec();
|
||||
let total_bytes = bytes.len();
|
||||
let global = self.global();
|
||||
let stream = ReadableStream::new_from_bytes(&global, bytes);
|
||||
let stream = ReadableStream::new_from_bytes(&global, bytes, can_gc);
|
||||
Some(ExtractedBody {
|
||||
stream,
|
||||
total_bytes: Some(total_bytes),
|
||||
|
|
|
@ -277,7 +277,7 @@ impl FetchResponseListener for FetchContext {
|
|||
|
||||
fn process_response_chunk(&mut self, _: RequestId, chunk: Vec<u8>) {
|
||||
let response = self.response_object.root();
|
||||
response.stream_chunk(chunk);
|
||||
response.stream_chunk(chunk, CanGc::note());
|
||||
}
|
||||
|
||||
fn process_response_eof(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue