diff --git a/components/script/body.rs b/components/script/body.rs index 6e2e7359fbc..2c84fdd6495 100644 --- a/components/script/body.rs +++ b/components/script/body.rs @@ -438,21 +438,21 @@ impl ExtractedBody { /// pub trait Extractable { - fn extract(&self, global: &GlobalScope) -> Fallible; + fn extract(&self, global: &GlobalScope, can_gc: CanGc) -> Fallible; } impl Extractable for BodyInit { // https://fetch.spec.whatwg.org/#concept-bodyinit-extract - fn extract(&self, global: &GlobalScope) -> Fallible { + fn extract(&self, global: &GlobalScope, can_gc: CanGc) -> Fallible { 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 { - fn extract(&self, global: &GlobalScope) -> Fallible { + fn extract(&self, global: &GlobalScope, can_gc: CanGc) -> Fallible { 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 { } impl Extractable for Blob { - fn extract(&self, _global: &GlobalScope) -> Fallible { + fn extract(&self, _global: &GlobalScope, can_gc: CanGc) -> Fallible { 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 { + fn extract(&self, global: &GlobalScope, can_gc: CanGc) -> Fallible { 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 { + fn extract(&self, global: &GlobalScope, can_gc: CanGc) -> Fallible { 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 { + fn extract(&self, global: &GlobalScope, can_gc: CanGc) -> Fallible { 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( // 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. diff --git a/components/script/dom/bindings/codegen/Bindings.conf b/components/script/dom/bindings/codegen/Bindings.conf index a2c216863f7..de0f67315d1 100644 --- a/components/script/dom/bindings/codegen/Bindings.conf +++ b/components/script/dom/bindings/codegen/Bindings.conf @@ -30,7 +30,7 @@ DOMInterfaces = { 'Blob': { 'weakReferenceable': True, - 'canGc': ['Slice', 'Text', 'ArrayBuffer'], + 'canGc': ['Slice', 'Text', 'ArrayBuffer', 'Stream'], }, 'Bluetooth': { diff --git a/components/script/dom/blob.rs b/components/script/dom/blob.rs index 310a531a7da..31c6fa64620 100644 --- a/components/script/dom/blob.rs +++ b/components/script/dom/blob.rs @@ -86,8 +86,8 @@ impl Blob { } /// - pub fn get_stream(&self) -> DomRoot { - self.global().get_blob_stream(&self.blob_id) + pub fn get_stream(&self, can_gc: CanGc) -> DomRoot { + self.global().get_blob_stream(&self.blob_id, can_gc) } } @@ -237,8 +237,8 @@ impl BlobMethods for Blob { } // - fn Stream(&self, _cx: JSContext) -> NonNull { - self.get_stream().get_js_stream() + fn Stream(&self, _cx: JSContext, can_gc: CanGc) -> NonNull { + self.get_stream(can_gc).get_js_stream() } // https://w3c.github.io/FileAPI/#slice-method-algo diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs index a972c7d7eb1..f7641d0bd15 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -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>) { +fn stream_handle_incoming(stream: &ReadableStream, bytes: Fallible>, 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 { } /// - pub fn get_blob_stream(&self, blob_id: &BlobId) -> DomRoot { + pub fn get_blob_stream(&self, blob_id: &BlobId, can_gc: CanGc) -> DomRoot { 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), diff --git a/components/script/dom/htmlformelement.rs b/components/script/dom/htmlformelement.rs index 50a70675ba4..53a38441b11 100644 --- a/components/script/dom/htmlformelement.rs +++ b/components/script/dom/htmlformelement.rs @@ -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; diff --git a/components/script/dom/readablestream.rs b/components/script/dom/readablestream.rs index 24272757071..76c81d2ed14 100644 --- a/components/script/dom/readablestream.rs +++ b/components/script/dom/readablestream.rs @@ -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) -> DomRoot { + pub fn new_from_bytes( + global: &GlobalScope, + bytes: Vec, + can_gc: CanGc, + ) -> DomRoot { 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) { + pub fn enqueue_native(&self, bytes: Vec, 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) { + fn enqueue_chunk( + &self, + cx: SafeJSContext, + stream: HandleObject, + mut chunk: Vec, + 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 { diff --git a/components/script/dom/request.rs b/components/script/dom/request.rs index cf4eda5d2c2..61f1f408260 100644 --- a/components/script/dom/request.rs +++ b/components/script/dom/request.rs @@ -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() { diff --git a/components/script/dom/response.rs b/components/script/dom/response.rs index 92a04fe796c..1f82ba255ce 100644 --- a/components/script/dom/response.rs +++ b/components/script/dom/response.rs @@ -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) { + pub fn stream_chunk(&self, chunk: Vec, 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); } } diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs index b9cc481087c..970012c12e9 100644 --- a/components/script/dom/xmlhttprequest.rs +++ b/components/script/dom/xmlhttprequest.rs @@ -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), diff --git a/components/script/fetch.rs b/components/script/fetch.rs index 78f3f28c4f2..8d1bbf1edc8 100644 --- a/components/script/fetch.rs +++ b/components/script/fetch.rs @@ -277,7 +277,7 @@ impl FetchResponseListener for FetchContext { fn process_response_chunk(&mut self, _: RequestId, chunk: Vec) { let response = self.response_object.root(); - response.stream_chunk(chunk); + response.stream_chunk(chunk, CanGc::note()); } fn process_response_eof(