1use std::cell::{Cell, RefCell};
6use std::collections::VecDeque;
7use std::mem;
8use std::ptr::{self};
9use std::rc::Rc;
10
11use dom_struct::dom_struct;
12use js::context::JSContext;
13use js::conversions::ToJSValConvertible;
14use js::jsapi::{Heap, JSObject};
15use js::jsval::{JSVal, ObjectValue, UndefinedValue};
16use js::realm::CurrentRealm;
17use js::rust::{
18 HandleObject as SafeHandleObject, HandleValue as SafeHandleValue,
19 MutableHandleValue as SafeMutableHandleValue,
20};
21use rustc_hash::FxHashMap;
22use script_bindings::cell::DomRefCell;
23use script_bindings::codegen::GenericBindings::MessagePortBinding::MessagePortMethods;
24use script_bindings::reflector::{Reflector, reflect_dom_object_with_proto};
25use script_bindings::structuredclone::StructuredData;
26use servo_base::id::{MessagePortId, MessagePortIndex};
27use servo_constellation_traits::MessagePortImpl;
28
29use crate::dom::bindings::callback::RootedCallback;
30use crate::dom::bindings::codegen::Bindings::QueuingStrategyBinding::{
31 QueuingStrategy, QueuingStrategySize,
32};
33use crate::dom::bindings::codegen::Bindings::UnderlyingSinkBinding::UnderlyingSink;
34use crate::dom::bindings::codegen::Bindings::WritableStreamBinding::WritableStreamMethods;
35use crate::dom::bindings::conversions::ConversionResult;
36use crate::dom::bindings::error::{Error, Fallible};
37use crate::dom::bindings::reflector::DomGlobal;
38use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
39use crate::dom::bindings::transferable::Transferable;
40use crate::dom::domexception::{DOMErrorName, DOMException};
41use crate::dom::globalscope::GlobalScope;
42use crate::dom::messageport::MessagePort;
43use crate::dom::promise::{Promise, RootedPromise, TracedPromise};
44use crate::dom::promisenativehandler::{Callback, PromiseNativeHandler};
45use crate::dom::readablestream::{ReadableStream, get_type_and_value_from_message};
46use crate::dom::stream::countqueuingstrategy::{extract_high_water_mark, extract_size_algorithm};
47use crate::dom::stream::writablestreamdefaultcontroller::{
48 UnderlyingSinkTypeRef, WritableStreamDefaultController,
49};
50use crate::dom::stream::writablestreamdefaultwriter::WritableStreamDefaultWriter;
51use crate::realms::enter_auto_realm;
52
53impl js::gc::Rootable for AbortAlgorithmFulfillmentHandler {}
54
55#[derive(JSTraceable, MallocSizeOf)]
58#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
59struct AbortAlgorithmFulfillmentHandler {
60 stream: Dom<WritableStream>,
61 abort_request_promise: TracedPromise,
62}
63
64impl Callback for AbortAlgorithmFulfillmentHandler {
65 fn callback(&self, cx: &mut CurrentRealm, _v: SafeHandleValue) {
66 self.abort_request_promise.resolve_native(cx, &());
68
69 self.stream
71 .as_rooted()
72 .reject_close_and_closed_promise_if_needed(cx);
73 }
74}
75
76impl js::gc::Rootable for AbortAlgorithmRejectionHandler {}
77
78#[derive(JSTraceable, MallocSizeOf)]
81#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
82struct AbortAlgorithmRejectionHandler {
83 stream: Dom<WritableStream>,
84 abort_request_promise: TracedPromise,
85}
86
87impl Callback for AbortAlgorithmRejectionHandler {
88 fn callback(&self, cx: &mut CurrentRealm, reason: SafeHandleValue) {
89 self.abort_request_promise.reject_native(cx, &reason);
91
92 self.stream
94 .as_rooted()
95 .reject_close_and_closed_promise_if_needed(cx);
96 }
97}
98
99impl js::gc::Rootable for PendingAbortRequest {}
100
101#[derive(JSTraceable, MallocSizeOf)]
103#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
104struct PendingAbortRequest {
105 promise: TracedPromise,
107
108 #[ignore_malloc_size_of = "mozjs"]
110 reason: Box<Heap<JSVal>>,
111
112 was_already_erroring: bool,
114}
115
116#[derive(Clone, Copy, Debug, Default, JSTraceable, MallocSizeOf)]
118pub(crate) enum WritableStreamState {
119 #[default]
120 Writable,
121 Closed,
122 Erroring,
123 Errored,
124}
125
126#[dom_struct]
128pub struct WritableStream {
129 reflector_: Reflector,
130
131 backpressure: Cell<bool>,
133
134 close_request: DomRefCell<Option<TracedPromise>>,
136
137 controller: MutNullableDom<WritableStreamDefaultController>,
139
140 detached: Cell<bool>,
142
143 in_flight_write_request: DomRefCell<Option<TracedPromise>>,
145
146 in_flight_close_request: DomRefCell<Option<TracedPromise>>,
148
149 pending_abort_request: DomRefCell<Option<PendingAbortRequest>>,
151
152 state: Cell<WritableStreamState>,
154
155 #[ignore_malloc_size_of = "mozjs"]
157 stored_error: Heap<JSVal>,
158
159 writer: MutNullableDom<WritableStreamDefaultWriter>,
161
162 write_requests: DomRefCell<VecDeque<TracedPromise>>,
164}
165
166impl WritableStream {
167 fn new_inherited() -> WritableStream {
169 WritableStream {
170 reflector_: Reflector::new(),
171 backpressure: Default::default(),
172 close_request: Default::default(),
173 controller: Default::default(),
174 detached: Default::default(),
175 in_flight_write_request: Default::default(),
176 in_flight_close_request: Default::default(),
177 pending_abort_request: Default::default(),
178 state: Default::default(),
179 stored_error: Default::default(),
180 writer: Default::default(),
181 write_requests: Default::default(),
182 }
183 }
184
185 pub(crate) fn new_with_proto(
186 cx: &mut JSContext,
187 global: &GlobalScope,
188 proto: Option<SafeHandleObject>,
189 ) -> DomRoot<WritableStream> {
190 reflect_dom_object_with_proto(cx, Box::new(WritableStream::new_inherited()), global, proto)
191 }
192
193 pub(crate) fn assert_no_controller(&self) {
196 assert!(self.controller.get().is_none());
197 }
198
199 pub(crate) fn set_default_controller(&self, controller: &WritableStreamDefaultController) {
202 self.controller.set(Some(controller));
203 }
204
205 pub(crate) fn get_default_controller(&self) -> DomRoot<WritableStreamDefaultController> {
206 self.controller.get().expect("Controller should be set.")
207 }
208
209 pub(crate) fn is_writable(&self) -> bool {
210 matches!(self.state.get(), WritableStreamState::Writable)
211 }
212
213 pub(crate) fn is_erroring(&self) -> bool {
214 matches!(self.state.get(), WritableStreamState::Erroring)
215 }
216
217 pub(crate) fn is_errored(&self) -> bool {
218 matches!(self.state.get(), WritableStreamState::Errored)
219 }
220
221 pub(crate) fn is_closed(&self) -> bool {
222 matches!(self.state.get(), WritableStreamState::Closed)
223 }
224
225 pub(crate) fn has_in_flight_write_request(&self) -> bool {
226 self.in_flight_write_request.borrow().is_some()
227 }
228
229 pub(crate) fn has_operations_marked_inflight(&self) -> bool {
231 let in_flight_write_requested = self.in_flight_write_request.borrow().is_some();
232 let in_flight_close_requested = self.in_flight_close_request.borrow().is_some();
233
234 in_flight_write_requested || in_flight_close_requested
235 }
236
237 pub(crate) fn get_stored_error(&self, mut handle_mut: SafeMutableHandleValue) {
239 handle_mut.set(self.stored_error.get());
240 }
241
242 pub(crate) fn finish_erroring(&self, cx: &mut JSContext, global: &GlobalScope) {
244 assert!(self.is_erroring());
246
247 assert!(!self.has_operations_marked_inflight());
249
250 self.state.set(WritableStreamState::Errored);
252
253 let Some(controller) = self.controller.get() else {
255 unreachable!("Stream should have a controller.");
256 };
257 controller.perform_error_steps();
258
259 rooted!(&in(cx) let mut stored_error = UndefinedValue());
261 self.get_stored_error(stored_error.handle_mut());
262
263 rooted!(&in(cx) let write_requests = mem::take(&mut *self.write_requests.borrow_mut()));
265 for request in write_requests.iter() {
266 request.reject(cx, stored_error.handle());
268 }
269
270 if self.pending_abort_request.borrow().is_none() {
275 self.reject_close_and_closed_promise_if_needed(cx);
277
278 return;
280 }
281
282 rooted!(&in(cx) let pending_abort_request = self.pending_abort_request.borrow_mut().take());
285 if let Some(pending_abort_request) = &*pending_abort_request {
286 if pending_abort_request.was_already_erroring {
288 pending_abort_request
290 .promise
291 .reject(cx, stored_error.handle());
292
293 self.reject_close_and_closed_promise_if_needed(cx);
295
296 return;
298 }
299
300 rooted!(&in(cx) let mut reason = UndefinedValue());
302 reason.set(pending_abort_request.reason.get());
303 let promise = controller.abort_steps(cx, global, reason.handle());
304
305 rooted!(&in(cx) let mut fulfillment_handler = Some(AbortAlgorithmFulfillmentHandler {
307 stream: Dom::from_ref(self),
308 abort_request_promise: pending_abort_request.promise.clone(),
309 }));
310
311 rooted!(&in(cx) let mut rejection_handler = Some(AbortAlgorithmRejectionHandler {
313 stream: Dom::from_ref(self),
314 abort_request_promise: pending_abort_request.promise.clone(),
315 }));
316
317 let handler = PromiseNativeHandler::new(
318 cx,
319 global,
320 fulfillment_handler.take().map(|h| Box::new(h) as Box<_>),
321 rejection_handler.take().map(|h| Box::new(h) as Box<_>),
322 );
323
324 let mut realm = enter_auto_realm(cx, global);
325 let cx = &mut realm.current_realm();
326 promise.append_native_handler(cx, &handler);
327 }
328 }
329
330 fn reject_close_and_closed_promise_if_needed(&self, cx: &mut JSContext) {
332 assert!(self.is_errored());
334
335 rooted!(&in(cx) let mut stored_error = UndefinedValue());
336 self.get_stored_error(stored_error.handle_mut());
337
338 rooted!(&in(cx) let close_request = self.close_request.borrow_mut().take());
340 if let Some(ref close_request) = *close_request {
341 assert!(self.in_flight_close_request.borrow().is_none());
343
344 close_request.reject_native(cx, &stored_error.handle())
346
347 }
350
351 if let Some(writer) = self.writer.get() {
354 writer.reject_closed_promise_with_stored_error(cx, &stored_error.handle());
356
357 writer.set_close_promise_is_handled(cx);
359 }
360 }
361
362 pub(crate) fn close_queued_or_in_flight(&self) -> bool {
364 let close_requested = self.close_request.borrow().is_some();
365 let in_flight_close_requested = self.in_flight_close_request.borrow().is_some();
366
367 close_requested || in_flight_close_requested
368 }
369
370 pub(crate) fn finish_in_flight_write(&self, cx: &mut JSContext) {
372 rooted!(&in(cx) let in_flight_write_request = self.in_flight_write_request.borrow_mut().take());
373 let Some(ref in_flight_write_request) = *in_flight_write_request else {
374 unreachable!("Stream should have a write request");
376 };
377
378 in_flight_write_request.resolve_native(cx, &());
380
381 }
384
385 pub(crate) fn start_erroring(
387 &self,
388 cx: &mut JSContext,
389 global: &GlobalScope,
390 error: SafeHandleValue,
391 ) {
392 assert!(self.stored_error.get().is_undefined());
394
395 assert!(self.is_writable());
397
398 let Some(controller) = self.controller.get() else {
400 unreachable!("Stream should have a controller.");
402 };
403
404 self.state.set(WritableStreamState::Erroring);
406
407 self.stored_error.set(*error);
409
410 if let Some(writer) = self.writer.get() {
412 writer.ensure_ready_promise_rejected(cx, global, error);
414 }
415
416 if !self.has_operations_marked_inflight() && controller.started() {
418 self.finish_erroring(cx, global);
420 }
421 }
422
423 pub(crate) fn deal_with_rejection(
425 &self,
426 cx: &mut JSContext,
427 global: &GlobalScope,
428 error: SafeHandleValue,
429 ) {
430 if self.is_writable() {
434 self.start_erroring(cx, global, error);
436
437 return;
439 }
440
441 assert!(self.is_erroring());
443
444 self.finish_erroring(cx, global);
446 }
447
448 pub(crate) fn mark_first_write_request_in_flight(&self) {
450 let mut in_flight_write_request = self.in_flight_write_request.borrow_mut();
451 let mut write_requests = self.write_requests.borrow_mut();
452
453 assert!(in_flight_write_request.is_none());
455
456 assert!(!write_requests.is_empty());
458
459 *in_flight_write_request = write_requests.pop_front();
463 }
464
465 pub(crate) fn mark_close_request_in_flight(&self) {
467 let mut in_flight_close_request = self.in_flight_close_request.borrow_mut();
468 let mut close_request = self.close_request.borrow_mut();
469
470 assert!(in_flight_close_request.is_none());
472
473 assert!(close_request.is_some());
475
476 *in_flight_close_request = close_request.take();
480 }
481
482 pub(crate) fn finish_in_flight_close(&self, cx: &mut JSContext) {
484 rooted!(&in(cx) let in_flight_close_request = self.in_flight_close_request.borrow_mut().take());
485 let Some(ref in_flight_close_request) = *in_flight_close_request else {
486 unreachable!("in_flight_close_request must be Some");
488 };
489
490 in_flight_close_request.resolve_native(cx, &());
492
493 assert!(self.is_writable() || self.is_erroring());
498
499 if self.is_erroring() {
501 self.stored_error.set(UndefinedValue());
503
504 rooted!(&in(cx) let pending_abort_request = self.pending_abort_request.borrow_mut().take());
506 if let Some(pending_abort_request) = &*pending_abort_request {
507 pending_abort_request.promise.resolve_native(cx, &());
509
510 }
513 }
514
515 self.state.set(WritableStreamState::Closed);
517
518 if let Some(writer) = self.writer.get() {
520 writer.resolve_closed_promise_with_undefined(cx);
523 }
524
525 assert!(self.pending_abort_request.borrow().is_none());
527
528 assert!(self.stored_error.get().is_undefined());
530 }
531
532 pub(crate) fn finish_in_flight_close_with_error(
534 &self,
535 cx: &mut JSContext,
536 global: &GlobalScope,
537 error: SafeHandleValue,
538 ) {
539 rooted!(&in(cx) let in_flight_close_request = self.in_flight_close_request.borrow_mut().take());
540 let Some(ref in_flight_close_request) = *in_flight_close_request else {
541 unreachable!("Inflight close request must be defined.");
543 };
544
545 in_flight_close_request.reject_native(cx, &error);
547
548 assert!(self.is_erroring() || self.is_writable());
553
554 rooted!(&in(cx) let pending_abort_request = self.pending_abort_request.borrow_mut().take());
556 if let Some(pending_abort_request) = &*pending_abort_request {
557 pending_abort_request.promise.reject_native(cx, &error);
559
560 }
563
564 self.deal_with_rejection(cx, global, error);
566 }
567
568 pub(crate) fn finish_in_flight_write_with_error(
570 &self,
571 cx: &mut JSContext,
572 global: &GlobalScope,
573 error: SafeHandleValue,
574 ) {
575 rooted!(&in(cx) let in_flight_write_request = self.in_flight_write_request.borrow_mut().take());
576 let Some(ref in_flight_write_request) = *in_flight_write_request else {
577 unreachable!("Inflight write request must be defined.");
579 };
580
581 in_flight_write_request.reject_native(cx, &error);
583
584 assert!(self.is_erroring() || self.is_writable());
589
590 self.deal_with_rejection(cx, global, error);
592 }
593
594 pub(crate) fn get_writer(&self) -> Option<DomRoot<WritableStreamDefaultWriter>> {
595 self.writer.get()
596 }
597
598 pub(crate) fn set_writer(&self, writer: Option<&WritableStreamDefaultWriter>) {
599 self.writer.set(writer);
600 }
601
602 pub(crate) fn set_backpressure(&self, backpressure: bool) {
603 self.backpressure.set(backpressure);
604 }
605
606 pub(crate) fn get_backpressure(&self) -> bool {
607 self.backpressure.get()
608 }
609
610 pub(crate) fn is_locked(&self) -> bool {
612 self.get_writer().is_some()
615 }
616
617 pub(crate) fn add_write_request(
619 &self,
620 cx: &mut JSContext,
621 global: &GlobalScope,
622 ) -> RootedPromise {
623 assert!(self.is_locked());
625
626 assert!(self.is_writable());
628
629 let promise = Promise::new(cx, global);
631
632 self.write_requests
634 .borrow_mut()
635 .push_back(promise.to_traced());
636
637 promise
639 }
640
641 pub(crate) fn get_controller(&self) -> Option<DomRoot<WritableStreamDefaultController>> {
643 self.controller.get()
644 }
645
646 pub(crate) fn abort(
648 &self,
649 cx: &mut CurrentRealm,
650 global: &GlobalScope,
651 provided_reason: SafeHandleValue,
652 ) -> RootedPromise {
653 if self.is_closed() || self.is_errored() {
655 return Promise::new_resolved(cx, global, ());
657 }
658
659 self.get_controller()
661 .expect("Stream must have a controller.")
662 .signal_abort(cx, provided_reason);
663
664 let state = self.state.get();
666
667 if matches!(
669 state,
670 WritableStreamState::Closed | WritableStreamState::Errored
671 ) {
672 return Promise::new_resolved(cx, global, ());
673 }
674
675 if self.pending_abort_request.borrow().is_some() {
677 return self
679 .pending_abort_request
680 .borrow()
681 .as_ref()
682 .expect("Pending abort request must be Some.")
683 .promise
684 .root(cx);
685 }
686
687 assert!(self.is_writable() || self.is_erroring());
689
690 let mut was_already_erroring = false;
692 rooted!(&in(cx) let undefined_reason = UndefinedValue());
693
694 let reason = if self.is_erroring() {
696 was_already_erroring = true;
698
699 undefined_reason.handle()
701 } else {
702 provided_reason
704 };
705
706 let promise = Promise::new(cx, global);
708
709 *self.pending_abort_request.borrow_mut() = Some(PendingAbortRequest {
714 promise: promise.to_traced(),
715 reason: Heap::boxed(reason.get()),
716 was_already_erroring,
717 });
718
719 if !was_already_erroring {
721 self.start_erroring(cx, global, reason);
723 }
724
725 promise
727 }
728
729 pub(crate) fn close(&self, cx: &mut JSContext, global: &GlobalScope) -> RootedPromise {
731 if self.is_closed() || self.is_errored() {
734 let promise = Promise::new(cx, global);
736 promise.reject_error(cx, Error::Type(c"Stream is closed or errored.".to_owned()));
737 return promise;
738 }
739
740 assert!(self.is_writable() || self.is_erroring());
742
743 assert!(!self.close_queued_or_in_flight());
745
746 let promise = Promise::new(cx, global);
748
749 *self.close_request.borrow_mut() = Some(promise.to_traced());
751
752 if let Some(writer) = self.writer.get() {
755 if self.get_backpressure() && self.is_writable() {
758 writer.resolve_ready_promise_with_undefined(cx);
760 }
761 }
762
763 let Some(controller) = self.controller.get() else {
765 unreachable!("Stream must have a controller.");
766 };
767 controller.close(cx, global);
768
769 promise
771 }
772
773 pub(crate) fn get_desired_size(&self) -> Option<f64> {
776 if self.is_errored() || self.is_erroring() {
782 return None;
783 }
784
785 if self.is_closed() {
787 return Some(0.);
788 }
789
790 let Some(controller) = self.controller.get() else {
791 unreachable!("Stream must have a controller.");
792 };
793 Some(controller.get_desired_size())
794 }
795
796 pub(crate) fn aquire_default_writer(
798 &self,
799 cx: &mut CurrentRealm,
800 global: &GlobalScope,
801 ) -> Result<DomRoot<WritableStreamDefaultWriter>, Error> {
802 let writer = WritableStreamDefaultWriter::new(cx, global, None);
804
805 writer.setup(cx, self)?;
807
808 Ok(writer)
810 }
811
812 pub(crate) fn update_backpressure(
814 &self,
815 cx: &mut JSContext,
816 backpressure: bool,
817 global: &GlobalScope,
818 ) {
819 self.is_writable();
821
822 assert!(!self.close_queued_or_in_flight());
824
825 let writer = self.get_writer();
827
828 if let Some(writer) = writer {
829 if backpressure != self.get_backpressure() {
831 if backpressure {
833 let promise = Promise::new(cx, global);
835 writer.set_ready_promise(&promise);
836 } else {
837 assert!(!backpressure);
840 writer.resolve_ready_promise_with_undefined(cx);
842 }
843 }
844 }
845
846 self.set_backpressure(backpressure);
848 }
849
850 pub(crate) fn setup_cross_realm_transform_writable(
852 &self,
853 cx: &mut JSContext,
854 port: &MessagePort,
855 ) {
856 let port_id = port.message_port_id();
857 let global = self.global();
858
859 let size_algorithm = extract_size_algorithm(cx, &QueuingStrategy::default());
865
866 let backpressure_promise = Promise::new(cx, &global);
870 rooted!(&in(cx) let backpressure_promise = RcHolder(Rc::new(RefCell::new(Some(backpressure_promise.to_traced())))));
871
872 let controller = WritableStreamDefaultController::new(
874 cx,
875 &global,
876 UnderlyingSinkTypeRef::Transfer {
877 backpressure_promise: &backpressure_promise.0,
878 port,
879 },
880 1.0,
881 size_algorithm,
882 );
883
884 rooted!(&in(cx) let cross_realm_transform_writable = CrossRealmTransformWritable {
887 controller: Dom::from_ref(&controller),
888 backpressure_promise: backpressure_promise.0.clone(),
889 });
890 global.note_cross_realm_transform_writable(&cross_realm_transform_writable, port_id);
891
892 port.Start(cx);
894
895 controller
897 .setup(cx, &global, self)
898 .expect("Setup for transfer cannot fail");
899 }
900 #[allow(clippy::too_many_arguments)]
902 fn setup_from_underlying_sink(
903 &self,
904 cx: &mut JSContext,
905 global: &GlobalScope,
906 stream: &WritableStream,
907 underlying_sink_obj: SafeHandleObject,
908 underlying_sink: &UnderlyingSink,
909 strategy_hwm: f64,
910 strategy_size: RootedCallback<QueuingStrategySize>,
911 ) -> Result<(), Error> {
912 let controller = WritableStreamDefaultController::new(
938 cx,
939 global,
940 UnderlyingSinkTypeRef::new_js(
941 underlying_sink.abort.as_ref(),
942 underlying_sink.start.as_ref(),
943 underlying_sink.close.as_ref(),
944 underlying_sink.write.as_ref(),
945 ),
946 strategy_hwm,
947 strategy_size,
948 );
949
950 controller.set_underlying_sink_this_object(underlying_sink_obj);
953
954 controller.setup(cx, global, stream)
956 }
957}
958
959pub(crate) fn create_writable_stream(
961 cx: &mut JSContext,
962 global: &GlobalScope,
963 writable_high_water_mark: f64,
964 writable_size_algorithm: RootedCallback<QueuingStrategySize>,
965 underlying_sink_type: UnderlyingSinkTypeRef<'_>,
966) -> Fallible<DomRoot<WritableStream>> {
967 assert!(writable_high_water_mark >= 0.0);
969
970 let stream = WritableStream::new_with_proto(cx, global, None);
973
974 let controller = WritableStreamDefaultController::new(
976 cx,
977 global,
978 underlying_sink_type,
979 writable_high_water_mark,
980 writable_size_algorithm,
981 );
982
983 controller.setup(cx, global, &stream)?;
986
987 Ok(stream)
989}
990
991impl WritableStreamMethods<crate::DomTypeHolder> for WritableStream {
992 fn Constructor(
994 cx: &mut JSContext,
995 global: &GlobalScope,
996 proto: Option<SafeHandleObject>,
997 underlying_sink: Option<*mut JSObject>,
998 strategy: &QueuingStrategy,
999 ) -> Fallible<DomRoot<WritableStream>> {
1000 rooted!(&in(cx) let underlying_sink_obj = underlying_sink.unwrap_or(ptr::null_mut()));
1002
1003 let underlying_sink_dict = if !underlying_sink_obj.is_null() {
1006 rooted!(&in(cx) let obj_val = ObjectValue(underlying_sink_obj.get()));
1007 match UnderlyingSink::new(cx, obj_val.handle()) {
1008 Ok(ConversionResult::Success(val)) => val,
1009 Ok(ConversionResult::Failure(error)) => {
1010 return Err(Error::Type(error.into_owned()));
1011 },
1012 _ => {
1013 return Err(Error::JSFailed);
1014 },
1015 }
1016 } else {
1017 UnderlyingSink::empty()
1018 };
1019
1020 if !underlying_sink_dict.type_.get().is_undefined() {
1021 return Err(Error::Range(c"type is set".to_owned()));
1023 }
1024
1025 let stream = WritableStream::new_with_proto(cx, global, proto);
1027
1028 let size_algorithm = extract_size_algorithm(cx, strategy);
1030
1031 let high_water_mark = extract_high_water_mark(strategy, 1.0)?;
1033
1034 stream.setup_from_underlying_sink(
1037 cx,
1038 global,
1039 &stream,
1040 underlying_sink_obj.handle(),
1041 &underlying_sink_dict,
1042 high_water_mark,
1043 size_algorithm,
1044 )?;
1045
1046 Ok(stream)
1047 }
1048
1049 fn Locked(&self) -> bool {
1051 self.is_locked()
1053 }
1054
1055 fn Abort(&self, cx: &mut CurrentRealm, reason: SafeHandleValue) -> RootedPromise {
1057 let global = GlobalScope::from_current_realm(cx);
1058
1059 if self.is_locked() {
1061 let promise = Promise::new(cx, &global);
1063 promise.reject_error(cx, Error::Type(c"Stream is locked.".to_owned()));
1064 return promise;
1065 }
1066
1067 self.abort(cx, &global, reason)
1069 }
1070
1071 fn Close(&self, cx: &mut CurrentRealm) -> RootedPromise {
1073 let global = GlobalScope::from_current_realm(cx);
1074
1075 if self.is_locked() {
1077 let promise = Promise::new(cx, &global);
1079 promise.reject_error(cx, Error::Type(c"Stream is locked.".to_owned()));
1080 return promise;
1081 }
1082
1083 if self.close_queued_or_in_flight() {
1085 let promise = Promise::new(cx, &global);
1087 promise.reject_error(
1088 cx,
1089 Error::Type(c"Stream has closed queued or in-flight".to_owned()),
1090 );
1091 return promise;
1092 }
1093
1094 self.close(cx, &global)
1096 }
1097
1098 fn GetWriter(
1100 &self,
1101 realm: &mut CurrentRealm,
1102 ) -> Result<DomRoot<WritableStreamDefaultWriter>, Error> {
1103 let global = GlobalScope::from_current_realm(realm);
1104
1105 self.aquire_default_writer(realm, &global)
1107 }
1108}
1109
1110impl js::gc::Rootable for CrossRealmTransformWritable {}
1111
1112#[derive(Clone, JSTraceable, MallocSizeOf)]
1116#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
1117pub(crate) struct CrossRealmTransformWritable {
1118 controller: Dom<WritableStreamDefaultController>,
1120
1121 #[conditional_malloc_size_of]
1123 backpressure_promise: Rc<RefCell<Option<TracedPromise>>>,
1124}
1125
1126impl CrossRealmTransformWritable {
1127 pub(crate) fn handle_message(
1130 &self,
1131 cx: &mut CurrentRealm,
1132 global: &GlobalScope,
1133 message: SafeHandleValue,
1134 ) {
1135 rooted!(&in(cx) let mut value = UndefinedValue());
1136 let type_string = get_type_and_value_from_message(cx, message, value.handle_mut());
1137
1138 if type_string == "error" {
1143 self.controller.error_if_needed(cx, value.handle(), global);
1145 }
1146
1147 rooted!(&in(cx) let backpressure_promise = self.backpressure_promise.borrow_mut().take());
1148
1149 if let Some(ref promise) = *backpressure_promise {
1152 promise.resolve_native(cx, &());
1154
1155 }
1158 }
1159
1160 pub(crate) fn handle_error(
1163 &self,
1164 cx: &mut CurrentRealm,
1165 global: &GlobalScope,
1166 port: &MessagePort,
1167 ) {
1168 let error = DOMException::new(cx, global, DOMErrorName::DataCloneError);
1170 rooted!(&in(cx) let mut rooted_error = UndefinedValue());
1171 error.to_jsval(cx, rooted_error.handle_mut());
1172
1173 port.cross_realm_transform_send_error(cx, rooted_error.handle());
1175
1176 self.controller
1178 .error_if_needed(cx, rooted_error.handle(), global);
1179
1180 global.disentangle_port(cx, port);
1182 }
1183}
1184
1185impl Transferable for WritableStream {
1187 type Index = MessagePortIndex;
1188 type Data = MessagePortImpl;
1189
1190 fn transfer(&self, cx: &mut JSContext) -> Fallible<(MessagePortId, MessagePortImpl)> {
1192 if self.is_locked() {
1195 return Err(Error::DataClone(None));
1196 }
1197
1198 let global = self.global();
1199 let mut realm = enter_auto_realm(cx, &*global);
1200 let mut realm = realm.current_realm();
1201 let cx = &mut realm;
1202
1203 let port_1 = MessagePort::new(cx, &global);
1205 global.track_message_port(&port_1, None);
1206
1207 let port_2 = MessagePort::new(cx, &global);
1209 global.track_message_port(&port_2, None);
1210
1211 global.entangle_ports(*port_1.message_port_id(), *port_2.message_port_id());
1213
1214 let readable = ReadableStream::new_with_proto(cx, &global, None);
1216
1217 readable.setup_cross_realm_transform_readable(cx, &port_1);
1219
1220 let promise = readable.pipe_to(cx, &global, self, false, false, false, None);
1222
1223 promise.set_promise_is_handled(cx);
1225
1226 port_2.transfer(cx)
1228 }
1229
1230 fn transfer_receive(
1232 cx: &mut JSContext,
1233 owner: &GlobalScope,
1234 id: MessagePortId,
1235 port_impl: MessagePortImpl,
1236 ) -> Result<DomRoot<Self>, ()> {
1237 let value = WritableStream::new_with_proto(cx, owner, None);
1240
1241 let transferred_port = MessagePort::transfer_receive(cx, owner, id, port_impl)?;
1248
1249 value.setup_cross_realm_transform_writable(cx, &transferred_port);
1251 Ok(value)
1252 }
1253
1254 fn serialized_storage<'a>(
1256 data: StructuredData<'a, '_>,
1257 ) -> &'a mut Option<FxHashMap<MessagePortId, Self::Data>> {
1258 match data {
1259 StructuredData::Reader(r) => &mut r.port_impls,
1260 StructuredData::Writer(w) => &mut w.ports,
1261 }
1262 }
1263}
1264
1265#[derive(JSTraceable)]
1266struct RcHolder<T>(Rc<T>);
1267
1268impl<T: js::rust::Traceable> js::gc::Rootable for RcHolder<T> {}