/
githubmirror
/
servo
Обзор
Документация
Войти
/
githubmirror
/
servo
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
components/script/dom/stream/underlyingsourcecontainer.rs
337 строк
14 KB
Tim van der Lippe
script: Pass `&mut JSContext` to Promise creation (#45887)
22 июн 2026, 18:30
Не верифицирован
22 июн 2026, 18:30
43defd4
Код
Авторство
О чём код?
/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::ptr; use std::rc::Rc; use dom_struct::dom_struct; use js::context::JSContext; use js::jsapi::{Heap, IsPromiseObject, JSObject}; use js::jsval::{JSVal, UndefinedValue}; use js::rust::{Handle as SafeHandle, HandleObject, HandleValue as SafeHandleValue, IntoHandle}; use script_bindings::reflector::{Reflector, reflect_dom_object_with_cx}; use super::byteteeunderlyingsource::ByteTeeUnderlyingSource; use crate::dom::bindings::callback::ExceptionHandling; use crate::dom::bindings::codegen::Bindings::UnderlyingSourceBinding::UnderlyingSource as JsUnderlyingSource; use crate::dom::bindings::codegen::UnionTypes::ReadableStreamDefaultControllerOrReadableByteStreamController as Controller; use crate::dom::bindings::error::Error; use crate::dom::bindings::reflector::DomGlobal; use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::globalscope::GlobalScope; use crate::dom::messageport::MessagePort; use crate::dom::promise::Promise; use crate::dom::stream::defaultteeunderlyingsource::DefaultTeeUnderlyingSource; use crate::dom::stream::transformstream::TransformStream; /// A variation of [UnderlyingSourceType] used for storing state within UnderlyingContainer. /// All variants have identical meanings to [UnderlyingSourceType]. #[derive(JSTraceable)] #[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)] enum UnderlyingSource { Memory(usize), Blob(usize), FetchResponse, Js(JsUnderlyingSource, Heap<*mut JSObject>), Tee(Dom<DefaultTeeUnderlyingSource>), Transfer(Dom<MessagePort>), Transform(Dom<TransformStream>, Rc<Promise>), TeeByte(Dom<ByteTeeUnderlyingSource>), } impl UnderlyingSource { /// Does the source have all data in memory? fn in_memory(&self) -> bool { matches!(self, UnderlyingSource::Memory(_)) } } impl From<UnderlyingSourceType<'_>> for UnderlyingSource { fn from(underlying_source_type: UnderlyingSourceType<'_>) -> Self { match underlying_source_type { UnderlyingSourceType::Memory(size) => UnderlyingSource::Memory(size), UnderlyingSourceType::Blob(size) => UnderlyingSource::Blob(size), UnderlyingSourceType::FetchResponse => UnderlyingSource::FetchResponse, UnderlyingSourceType::Js(source) => UnderlyingSource::Js(source, Heap::default()), UnderlyingSourceType::Tee(source) => UnderlyingSource::Tee(Dom::from_ref(source)), UnderlyingSourceType::Transfer(port) => UnderlyingSource::Transfer(Dom::from_ref(port)), UnderlyingSourceType::Transform(stream, promise) => { UnderlyingSource::Transform(Dom::from_ref(stream), promise) }, UnderlyingSourceType::TeeByte(source) => { UnderlyingSource::TeeByte(Dom::from_ref(source)) }, } } } /// <https://streams.spec.whatwg.org/#underlying-source-api> /// The `Js` variant corresponds to /// the JavaScript object representing the underlying source. /// The other variants are native sources in Rust. pub(crate) enum UnderlyingSourceType<'a> { /// Facilitate partial integration with sources /// that are currently read into memory. Memory(usize), /// A blob as underlying source, with a known total size. Blob(usize), /// A fetch response as underlying source. FetchResponse, /// A struct representing a JS object as underlying source, /// and the actual JS object for use as `thisArg` in callbacks. Js(JsUnderlyingSource), /// Tee Tee(&'a DefaultTeeUnderlyingSource), /// Transfer, with the port used in some of the algorithms. Transfer(&'a MessagePort), /// A struct representing a JS object as underlying source, /// and the actual JS object for use as `thisArg` in callbacks. /// This is used for the `TransformStream` API. Transform(&'a TransformStream, Rc<Promise>), /// Tee Byte TeeByte(&'a ByteTeeUnderlyingSource), } impl UnderlyingSourceType<'_> { /// Is the source backed by a Rust native source? pub(crate) fn is_native(&self) -> bool { matches!( self, UnderlyingSourceType::Memory(_) | UnderlyingSourceType::Blob(_) | UnderlyingSourceType::FetchResponse | UnderlyingSourceType::Transfer(_) ) } } /// Wrapper around the underlying source. #[dom_struct] pub(crate) struct UnderlyingSourceContainer { reflector_: Reflector, #[ignore_malloc_size_of = "JsUnderlyingSource implemented in SM."] underlying_source_type: UnderlyingSource, } impl UnderlyingSourceContainer { fn new_inherited(underlying_source_type: UnderlyingSourceType) -> UnderlyingSourceContainer { UnderlyingSourceContainer { reflector_: Reflector::new(), underlying_source_type: underlying_source_type.into(), } } pub(crate) fn new( cx: &mut JSContext, global: &GlobalScope, underlying_source_type: UnderlyingSourceType, ) -> DomRoot<UnderlyingSourceContainer> { // TODO: setting the underlying source dict as the prototype of the // `UnderlyingSourceContainer`, as it is later used as the "this" in Call_. // Is this a good idea? reflect_dom_object_with_cx( Box::new(UnderlyingSourceContainer::new_inherited( underlying_source_type, )), global, cx, ) } /// Setting the JS object after the heap has settled down. pub(crate) fn set_underlying_source_this_object(&self, object: HandleObject) { if let UnderlyingSource::Js(_source, this_obj) = &self.underlying_source_type { this_obj.set(*object); } } /// <https://streams.spec.whatwg.org/#dom-underlyingsource-cancel> #[expect(unsafe_code)] pub(crate) fn call_cancel_algorithm( &self, cx: &mut JSContext, global: &GlobalScope, reason: SafeHandleValue, ) -> Option<Result<Rc<Promise>, Error>> { match &self.underlying_source_type { UnderlyingSource::Js(source, this_obj) => { if let Some(algo) = &source.cancel { let result = unsafe { algo.Call_( cx, &SafeHandle::from_raw(this_obj.handle()), Some(reason), ExceptionHandling::Rethrow, ) }; return Some(result); } None }, UnderlyingSource::Tee(tee_underlying_source) => { // Call the cancel algorithm for the appropriate branch. tee_underlying_source.cancel_algorithm(cx, global, reason) }, UnderlyingSource::Transform(stream, _) => { // Return ! TransformStreamDefaultSourceCancelAlgorithm(stream, reason). Some(stream.transform_stream_default_source_cancel(cx, global, reason)) }, UnderlyingSource::Transfer(port) => { // Let cancelAlgorithm be the following steps, taking a reason argument: // from <https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformreadable // Let result be PackAndPostMessageHandlingError(port, "error", reason). let result = port.pack_and_post_message_handling_error(cx, "error", reason); // Disentangle port. self.global().disentangle_port(cx, port); let promise = Promise::new(cx, &self.global()); // If result is an abrupt completion, if let Err(error) = result { // Return a promise rejected with result.[[Value]]. promise.reject_error(cx, error); } else { // Otherwise, return a promise resolved with undefined. promise.resolve_native(cx, &()); } Some(Ok(promise)) }, UnderlyingSource::TeeByte(tee_underlyin_source) => { // Call the cancel algorithm for the appropriate branch. tee_underlyin_source.cancel_algorithm(cx, reason) }, _ => None, } } /// <https://streams.spec.whatwg.org/#dom-underlyingsource-pull> #[expect(unsafe_code)] pub(crate) fn call_pull_algorithm( &self, cx: &mut JSContext, controller: Controller, ) -> Option<Result<Rc<Promise>, Error>> { match &self.underlying_source_type { UnderlyingSource::Js(source, this_obj) => { if let Some(algo) = &source.pull { let result = unsafe { algo.Call_( cx, &SafeHandle::from_raw(this_obj.handle()), controller, ExceptionHandling::Rethrow, ) }; return Some(result); } None }, UnderlyingSource::Tee(tee_underlying_source) => { // Call the pull algorithm for the appropriate branch. Some(Ok(tee_underlying_source.pull_algorithm(cx))) }, UnderlyingSource::Transfer(port) => { // Let pullAlgorithm be the following steps: // from <https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformreadable // Perform ! PackAndPostMessage(port, "pull", undefined). rooted!(&in(cx) let mut value = UndefinedValue()); port.pack_and_post_message(cx, "pull", value.handle()) .expect("Sending pull should not fail."); // Return a promise resolved with undefined. let promise = Promise::new_resolved(cx, &self.global(), ()); Some(Ok(promise)) }, UnderlyingSource::TeeByte(tee_underlyin_source) => { // Call the pull algorithm for the appropriate branch. Some(Ok(tee_underlyin_source.pull_algorithm(cx, None))) }, // Note: other source type have no pull steps for now. UnderlyingSource::Transform(stream, _) => { // Return ! TransformStreamDefaultSourcePullAlgorithm(stream). Some(stream.transform_stream_default_source_pull(cx, &self.global())) }, _ => None, } } /// <https://streams.spec.whatwg.org/#dom-underlyingsource-start> /// /// Note: The algorithm can return any value, including a promise, /// we always transform the result into a promise for convenience, /// and it is also how to spec deals with the situation. /// see "Let startPromise be a promise resolved with startResult." /// at <https://streams.spec.whatwg.org/#set-up-readable-stream-default-controller> #[expect(unsafe_code)] pub(crate) fn call_start_algorithm( &self, cx: &mut JSContext, controller: Controller, ) -> Option<Result<Rc<Promise>, Error>> { match &self.underlying_source_type { UnderlyingSource::Js(source, this_obj) => { if let Some(start) = &source.start { rooted!(&in(cx) let mut result_object = ptr::null_mut::<JSObject>()); rooted!(&in(cx) let mut result: JSVal); unsafe { if let Err(error) = start.Call_( cx, &SafeHandle::from_raw(this_obj.handle()), controller, result.handle_mut(), ExceptionHandling::Rethrow, ) { return Some(Err(error)); } } let is_promise = unsafe { if result.is_object() { result_object.set(result.to_object()); IsPromiseObject(result_object.handle().into_handle()) } else { false } }; let promise = if is_promise { Promise::new_with_js_promise(cx, result_object.handle()) } else { Promise::new_resolved(cx, &self.global(), result.get()) }; return Some(Ok(promise)); } None }, UnderlyingSource::Tee(_) => { // Let startAlgorithm be an algorithm that returns undefined. None }, UnderlyingSource::Transfer(_) => { // Let startAlgorithm be an algorithm that returns undefined. // from <https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformreadable None }, UnderlyingSource::Transform(_, start_promise) => { // Let startAlgorithm be an algorithm that returns startPromise. Some(Ok(start_promise.clone())) }, _ => None, } } /// <https://streams.spec.whatwg.org/#dom-underlyingsource-autoallocatechunksize> pub(crate) fn auto_allocate_chunk_size(&self) -> Option<u64> { match &self.underlying_source_type { UnderlyingSource::Js(source, _) => source.autoAllocateChunkSize, _ => None, } } /// Does the source have all data in memory? pub(crate) fn in_memory(&self) -> bool { self.underlying_source_type.in_memory() } }