From 9798373d5e27ab10645a230c08262d3675d3338e Mon Sep 17 00:00:00 2001 From: CYBAI Date: Sun, 7 Jun 2020 12:19:00 +0900 Subject: [PATCH 1/2] Enter incumbent script to ensure it has incumbent global --- components/script/dom/readablestream.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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)); From 63aab1b355d1ae0a40471d4259d5021c5bd02e1b Mon Sep 17 00:00:00 2001 From: CYBAI Date: Sun, 7 Jun 2020 12:31:48 +0900 Subject: [PATCH 2/2] Avoid sending a new chunk when the body is already done As discussed in https://github.com/servo/servo/issues/26807#issuecomment-640151804, we'd like to add a new flag, `in_memory_done`, to `TransmitBodyConnectHandler` so that we can correctly finish and drop the sender correctly. When we send the bytes, we will mark the body as done and we can recognize it's already done in next tick so that we can send a Done request to finish the sender. Also, when there comes a redirect request, it will go to `re-extract` route, we can set the `done` flag to `false` which means we won't stop the IPC routers yet. Then, if the re-extract sent its bytes, we will be marked as done again so that we can finish with stopping the IPC routes when Chunk request comes. --- components/net_traits/request.rs | 7 ++----- components/script/body.rs | 33 +++++++++++++++++++++++++++++--- 2 files changed, 32 insertions(+), 8 deletions(-) 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; }