diff --git a/components/net_traits/request.rs b/components/net_traits/request.rs index 27b9274a6dd..f80b82b0fca 100644 --- a/components/net_traits/request.rs +++ b/components/net_traits/request.rs @@ -117,7 +117,7 @@ pub enum ParserMetadata { } /// -#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] +#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)] pub enum BodySource { Null, Object, @@ -178,10 +178,7 @@ impl RequestBody { } pub fn source_is_null(&self) -> bool { - if let BodySource::Null = self.source { - return true; - } - false + self.source == BodySource::Null } pub fn len(&self) -> Option { diff --git a/components/script/body.rs b/components/script/body.rs index 8017519cdd3..ce7113f9807 100644 --- a/components/script/body.rs +++ b/components/script/body.rs @@ -55,7 +55,7 @@ pub enum BodySource { Null, /// Another Dom object as source, /// TODO: store the actual object - /// and re-exctact a stream on re-direct. + /// and re-extract a stream on re-direct. Object, } @@ -72,6 +72,7 @@ struct TransmitBodyConnectHandler { bytes_sender: Option>>, control_sender: IpcSender, in_memory: Option>, + in_memory_done: bool, source: BodySource, } @@ -91,14 +92,22 @@ impl TransmitBodyConnectHandler { bytes_sender: None, control_sender, in_memory, + in_memory_done: false, source, } } + /// Reset `in_memory_done`, called when a stream is + /// re-extracted from the source to support a re-direct. + pub fn reset_in_memory_done(&mut self) { + self.in_memory_done = false; + } + /// Re-extract the source to support streaming it again for a re-direct. /// TODO: actually re-extract the source, instead of just cloning data, to support Blob. - fn re_extract(&self, chunk_request_receiver: IpcReceiver) { + fn re_extract(&mut self, chunk_request_receiver: IpcReceiver) { let mut body_handler = self.clone(); + body_handler.reset_in_memory_done(); ROUTER.add_route( chunk_request_receiver.to_opaque(), @@ -126,11 +135,20 @@ impl TransmitBodyConnectHandler { /// TODO: this method should be deprecated /// in favor of making `re_extract` actually re-extract a stream from the source. /// See #26686 - fn transmit_source(&self) { + fn transmit_source(&mut self) { + if self.in_memory_done { + // Step 5.1.3 + self.stop_reading(); + return; + } + if let BodySource::Null = self.source { panic!("ReadableStream(Null) sources should not re-direct."); } + if let Some(bytes) = self.in_memory.clone() { + // The memoized bytes are sent so we mark it as done again + self.in_memory_done = true; let _ = self .bytes_sender .as_ref() @@ -155,6 +173,12 @@ impl TransmitBodyConnectHandler { /// The entry point to fn transmit_body_chunk(&mut self) { + if self.in_memory_done { + // Step 5.1.3 + self.stop_reading(); + return; + } + let stream = self.stream.clone(); let control_sender = self.control_sender.clone(); let bytes_sender = self @@ -165,6 +189,9 @@ impl TransmitBodyConnectHandler { // In case of the data being in-memory, send everything in one chunk, by-passing SpiderMonkey. if let Some(bytes) = self.in_memory.clone() { let _ = bytes_sender.send(bytes); + // Mark this body as `done` so that we can stop reading in the next tick, + // matching the behavior of the promise-based flow + self.in_memory_done = true; return; } diff --git a/components/script/dom/readablestream.rs b/components/script/dom/readablestream.rs index 1c6ac242e9b..90e6c72da70 100644 --- a/components/script/dom/readablestream.rs +++ b/components/script/dom/readablestream.rs @@ -6,7 +6,7 @@ use crate::dom::bindings::conversions::{ConversionBehavior, ConversionResult}; use crate::dom::bindings::error::Error; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; use crate::dom::bindings::root::DomRoot; -use crate::dom::bindings::settings_stack::AutoEntryScript; +use crate::dom::bindings::settings_stack::{AutoEntryScript, AutoIncumbentScript}; use crate::dom::bindings::utils::get_dictionary_property; use crate::dom::globalscope::GlobalScope; use crate::dom::promise::Promise; @@ -118,6 +118,7 @@ impl ReadableStream { source: ExternalUnderlyingSource, ) -> DomRoot { let _ar = enter_realm(global); + let _ais = AutoIncumbentScript::new(global); let cx = global.get_cx(); let source = Rc::new(ExternalUnderlyingSourceController::new(source));