From 40f9620aad721a52dc2f788c6e18fafb67944863 Mon Sep 17 00:00:00 2001 From: Gregory Terzian Date: Sun, 23 Feb 2020 01:25:17 +0800 Subject: [PATCH 1/9] start impl readablestream --- .../dom/bindings/codegen/parser/WebIDL.py | 28 +-------- components/script/dom/mod.rs | 1 + components/script/dom/readablestream.rs | 59 +++++++++++++++++++ .../script/dom/webidls/ReadableStream.webidl | 13 ++++ 4 files changed, 75 insertions(+), 26 deletions(-) create mode 100644 components/script/dom/readablestream.rs create mode 100644 components/script/dom/webidls/ReadableStream.webidl diff --git a/components/script/dom/bindings/codegen/parser/WebIDL.py b/components/script/dom/bindings/codegen/parser/WebIDL.py index b2e56c9deaf1..79825c9fa32d 100644 --- a/components/script/dom/bindings/codegen/parser/WebIDL.py +++ b/components/script/dom/bindings/codegen/parser/WebIDL.py @@ -2164,9 +2164,6 @@ def isSequence(self): def isRecord(self): return False - def isReadableStream(self): - return False - def isArrayBuffer(self): return False @@ -2198,8 +2195,7 @@ def isSpiderMonkeyInterface(self): return self.isInterface() and (self.isArrayBuffer() or self.isArrayBufferView() or self.isSharedArrayBuffer() or - self.isTypedArray() or - self.isReadableStream()) + self.isTypedArray()) def isDictionary(self): return False @@ -2401,9 +2397,6 @@ def isSequence(self): def isRecord(self): return self.inner.isRecord() - def isReadableStream(self): - return self.inner.isReadableStream() - def isArrayBuffer(self): return self.inner.isArrayBuffer() @@ -2783,9 +2776,6 @@ def isSequence(self): def isRecord(self): return self.inner.isRecord() - def isReadableStream(self): - return self.inner.isReadableStream() - def isDictionary(self): return self.inner.isDictionary() @@ -3121,7 +3111,6 @@ class IDLBuiltinType(IDLType): 'Uint32Array', 'Float32Array', 'Float64Array', - 'ReadableStream', ) TagLookup = { @@ -3158,7 +3147,6 @@ class IDLBuiltinType(IDLType): Types.Uint32Array: IDLType.Tags.interface, Types.Float32Array: IDLType.Tags.interface, Types.Float64Array: IDLType.Tags.interface, - Types.ReadableStream: IDLType.Tags.interface, } def __init__(self, location, name, type, clamp=False, enforceRange=False, treatNullAsEmpty=False, @@ -3258,9 +3246,6 @@ def isTypedArray(self): return (self._typeTag >= IDLBuiltinType.Types.Int8Array and self._typeTag <= IDLBuiltinType.Types.Float64Array) - def isReadableStream(self): - return self._typeTag == IDLBuiltinType.Types.ReadableStream - def isInterface(self): # TypedArray things are interface types per the TypedArray spec, # but we handle them as builtins because SpiderMonkey implements @@ -3268,8 +3253,7 @@ def isInterface(self): return (self.isArrayBuffer() or self.isArrayBufferView() or self.isSharedArrayBuffer() or - self.isTypedArray() or - self.isReadableStream()) + self.isTypedArray()) def isNonCallbackInterface(self): # All the interfaces we can be are non-callback @@ -3339,7 +3323,6 @@ def isDistinguishableFrom(self, other): # that's not an ArrayBuffer or a callback interface (self.isArrayBuffer() and not other.isArrayBuffer()) or (self.isSharedArrayBuffer() and not other.isSharedArrayBuffer()) or - (self.isReadableStream() and not other.isReadableStream()) or # ArrayBufferView is distinguishable from everything # that's not an ArrayBufferView or typed array. (self.isArrayBufferView() and not other.isArrayBufferView() and @@ -3492,9 +3475,6 @@ def withExtendedAttributes(self, attrs): IDLBuiltinType.Types.Float64Array: IDLBuiltinType(BuiltinLocation(""), "Float64Array", IDLBuiltinType.Types.Float64Array), - IDLBuiltinType.Types.ReadableStream: - IDLBuiltinType(BuiltinLocation(""), "ReadableStream", - IDLBuiltinType.Types.ReadableStream), } @@ -5715,7 +5695,6 @@ def t_OTHER(self, t): "setlike": "SETLIKE", "iterable": "ITERABLE", "namespace": "NAMESPACE", - "ReadableStream": "READABLESTREAM", "constructor": "CONSTRUCTOR", "symbol": "SYMBOL", "async": "ASYNC", @@ -7051,7 +7030,6 @@ def p_DistinguishableType(self, p): DistinguishableType : PrimitiveType Null | ARRAYBUFFER Null | SHAREDARRAYBUFFER Null - | READABLESTREAM Null | OBJECT Null """ if p[1] == "object": @@ -7060,8 +7038,6 @@ def p_DistinguishableType(self, p): type = BuiltinTypes[IDLBuiltinType.Types.ArrayBuffer] elif p[1] == "SharedArrayBuffer": type = BuiltinTypes[IDLBuiltinType.Types.SharedArrayBuffer] - elif p[1] == "ReadableStream": - type = BuiltinTypes[IDLBuiltinType.Types.ReadableStream] else: type = BuiltinTypes[p[1]] diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 2401c5a55cb0..4bf334899996 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -470,6 +470,7 @@ pub mod promiserejectionevent; pub mod radionodelist; pub mod range; pub mod raredata; +pub mod readablestream; pub mod request; pub mod response; pub mod rtcicecandidate; diff --git a/components/script/dom/readablestream.rs b/components/script/dom/readablestream.rs new file mode 100644 index 000000000000..56d6057ab5a4 --- /dev/null +++ b/components/script/dom/readablestream.rs @@ -0,0 +1,59 @@ +/* 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 crate::dom::bindings::codegen::Bindings::FunctionBinding::Function; +use crate::dom::bindings::codegen::Bindings::ReadableStreamBinding::Wrap; +use crate::dom::bindings::num::Finite; +use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; +use crate::dom::bindings::root::DomRoot; +use crate::dom::globalscope::GlobalScope; +use crate::js::conversions::ToJSValConvertible; +use crate::script_runtime::JSContext as SafeJSContext; +use js::jsapi::{Heap, JSFunction, JSObject}; +use js::jsapi::NewReadableDefaultStreamObject; +use js::jsval::UndefinedValue; +use dom_struct::dom_struct; +use std::rc::Rc; +use std::ptr; + +#[dom_struct] +pub struct ReadableStream { + reflector_: Reflector, + #[ignore_malloc_size_of = "SM handles JS values"] + stream: Heap<*mut JSObject>, +} + +impl ReadableStream { + /// + #[allow(non_snake_case, unsafe_code)] + pub fn Constructor( + cx: SafeJSContext, + global: &GlobalScope, + underlying_source: *mut JSObject, + _size: Rc, + high_watermark: Finite, + proto: *mut JSObject, + ) -> DomRoot { + let heap = Heap::default(); + + unsafe { + let source = Heap::boxed(underlying_source); + let proto = Heap::boxed(proto); + rooted!(in(*cx) let mut size_handler = ptr::null_mut::()); + let size = Heap::boxed(size_handler.get()); + + rooted!(in(*cx) let stream = NewReadableDefaultStreamObject(*cx, source.handle(), size.handle(), *high_watermark, proto.handle())); + heap.set(stream.get()); + } + + reflect_dom_object( + Box::new(ReadableStream { + reflector_: Reflector::new(), + stream: heap, + }), + global, + Wrap, + ) + } +} \ No newline at end of file diff --git a/components/script/dom/webidls/ReadableStream.webidl b/components/script/dom/webidls/ReadableStream.webidl new file mode 100644 index 000000000000..7ea0ef5f23b7 --- /dev/null +++ b/components/script/dom/webidls/ReadableStream.webidl @@ -0,0 +1,13 @@ +/* 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/. */ +/* + * The origin of this IDL file is: https://streams.spec.whatwg.org/#rs-class + */ + +[Exposed=(Window,Worker)] +interface ReadableStream { + constructor(object underlyingSource, Function size, HighWatermark highWaterMark, object proto); +}; + +typedef double HighWatermark; From a1ac06bc3cf6d01f5d85381daaf3a306b1647384 Mon Sep 17 00:00:00 2001 From: Gregory Terzian Date: Sun, 23 Feb 2020 19:29:27 +0800 Subject: [PATCH 2/9] use function, turn into handle function --- components/script/dom/readablestream.rs | 29 ++++++++++++++++++------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/components/script/dom/readablestream.rs b/components/script/dom/readablestream.rs index 56d6057ab5a4..5ecc23e48fd0 100644 --- a/components/script/dom/readablestream.rs +++ b/components/script/dom/readablestream.rs @@ -10,12 +10,13 @@ use crate::dom::bindings::root::DomRoot; use crate::dom::globalscope::GlobalScope; use crate::js::conversions::ToJSValConvertible; use crate::script_runtime::JSContext as SafeJSContext; -use js::jsapi::{Heap, JSFunction, JSObject}; +use dom_struct::dom_struct; use js::jsapi::NewReadableDefaultStreamObject; +use js::jsapi::{Heap, JSFunction, JSObject, JS_ValueToFunction}; use js::jsval::UndefinedValue; -use dom_struct::dom_struct; -use std::rc::Rc; +use js::rust::IntoHandle; use std::ptr; +use std::rc::Rc; #[dom_struct] pub struct ReadableStream { @@ -31,7 +32,7 @@ impl ReadableStream { cx: SafeJSContext, global: &GlobalScope, underlying_source: *mut JSObject, - _size: Rc, + size: Rc, high_watermark: Finite, proto: *mut JSObject, ) -> DomRoot { @@ -40,10 +41,22 @@ impl ReadableStream { unsafe { let source = Heap::boxed(underlying_source); let proto = Heap::boxed(proto); - rooted!(in(*cx) let mut size_handler = ptr::null_mut::()); - let size = Heap::boxed(size_handler.get()); - rooted!(in(*cx) let stream = NewReadableDefaultStreamObject(*cx, source.handle(), size.handle(), *high_watermark, proto.handle())); + rooted!(in(*cx) let mut size_val = UndefinedValue()); + size.to_jsval(*cx, size_val.handle_mut()); + + let size_func = Heap::boxed(JS_ValueToFunction(*cx, size_val.handle().into_handle())); + + rooted!(in(*cx) + let stream = NewReadableDefaultStreamObject( + *cx, + source.handle(), + size_func.handle(), + *high_watermark, + proto.handle() + ) + ); + heap.set(stream.get()); } @@ -56,4 +69,4 @@ impl ReadableStream { Wrap, ) } -} \ No newline at end of file +} From 81c8349978f37300cf64c6aaba724fd88aa962c4 Mon Sep 17 00:00:00 2001 From: Gregory Terzian Date: Sun, 23 Feb 2020 19:58:53 +0800 Subject: [PATCH 3/9] use handle instead of heap --- components/script/dom/readablestream.rs | 70 +++++++++++++++---------- 1 file changed, 43 insertions(+), 27 deletions(-) diff --git a/components/script/dom/readablestream.rs b/components/script/dom/readablestream.rs index 5ecc23e48fd0..6866a924ad8d 100644 --- a/components/script/dom/readablestream.rs +++ b/components/script/dom/readablestream.rs @@ -11,10 +11,10 @@ use crate::dom::globalscope::GlobalScope; use crate::js::conversions::ToJSValConvertible; use crate::script_runtime::JSContext as SafeJSContext; use dom_struct::dom_struct; -use js::jsapi::NewReadableDefaultStreamObject; use js::jsapi::{Heap, JSFunction, JSObject, JS_ValueToFunction}; +use js::jsapi::{IsReadableStream, NewReadableDefaultStreamObject}; use js::jsval::UndefinedValue; -use js::rust::IntoHandle; +use js::rust::{Handle, IntoHandle}; use std::ptr; use std::rc::Rc; @@ -25,9 +25,46 @@ pub struct ReadableStream { stream: Heap<*mut JSObject>, } +#[allow(unsafe_code)] +fn construct_default_readablestream( + cx: SafeJSContext, + underlying_source: *mut JSObject, + size: Rc, + high_watermark: Finite, + proto: *mut JSObject, +) -> Heap<*mut JSObject> { + let heap = Heap::default(); + let source = Handle::new(&underlying_source); + let proto = Handle::new(&proto); + + unsafe { + rooted!(in(*cx) let mut size_val = UndefinedValue()); + size.to_jsval(*cx, size_val.handle_mut()); + + let func = JS_ValueToFunction(*cx, size_val.handle().into_handle()); + let size_func = Handle::new(&func); + + rooted!(in(*cx) + let stream = NewReadableDefaultStreamObject( + *cx, + source.into_handle(), + size_func.into_handle(), + *high_watermark, + proto.into_handle() + ) + ); + + assert!(IsReadableStream(stream.get())); + + heap.set(stream.get()); + } + + heap +} + impl ReadableStream { /// - #[allow(non_snake_case, unsafe_code)] + #[allow(non_snake_case)] pub fn Constructor( cx: SafeJSContext, global: &GlobalScope, @@ -36,34 +73,13 @@ impl ReadableStream { high_watermark: Finite, proto: *mut JSObject, ) -> DomRoot { - let heap = Heap::default(); - - unsafe { - let source = Heap::boxed(underlying_source); - let proto = Heap::boxed(proto); - - rooted!(in(*cx) let mut size_val = UndefinedValue()); - size.to_jsval(*cx, size_val.handle_mut()); - - let size_func = Heap::boxed(JS_ValueToFunction(*cx, size_val.handle().into_handle())); - - rooted!(in(*cx) - let stream = NewReadableDefaultStreamObject( - *cx, - source.handle(), - size_func.handle(), - *high_watermark, - proto.handle() - ) - ); - - heap.set(stream.get()); - } + let stream = + construct_default_readablestream(cx, underlying_source, size, high_watermark, proto); reflect_dom_object( Box::new(ReadableStream { reflector_: Reflector::new(), - stream: heap, + stream, }), global, Wrap, From 31753d4579e547874e85bb0c1bf1f9796595f222 Mon Sep 17 00:00:00 2001 From: Gregory Terzian Date: Sun, 23 Feb 2020 21:03:17 +0800 Subject: [PATCH 4/9] cancel stream --- components/script/dom/readablestream.rs | 95 ++++++++++++++----- .../script/dom/webidls/ReadableStream.webidl | 1 + 2 files changed, 70 insertions(+), 26 deletions(-) diff --git a/components/script/dom/readablestream.rs b/components/script/dom/readablestream.rs index 6866a924ad8d..5509ece3a1ef 100644 --- a/components/script/dom/readablestream.rs +++ b/components/script/dom/readablestream.rs @@ -3,17 +3,22 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::dom::bindings::codegen::Bindings::FunctionBinding::Function; -use crate::dom::bindings::codegen::Bindings::ReadableStreamBinding::Wrap; +use crate::dom::bindings::codegen::Bindings::ReadableStreamBinding::{ReadableStreamMethods, Wrap}; +use crate::dom::bindings::error::{Error, Fallible}; use crate::dom::bindings::num::Finite; -use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; +use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::str::DOMString; use crate::dom::globalscope::GlobalScope; +use crate::dom::promise::Promise; use crate::js::conversions::ToJSValConvertible; use crate::script_runtime::JSContext as SafeJSContext; use dom_struct::dom_struct; -use js::jsapi::{Heap, JSFunction, JSObject, JS_ValueToFunction}; -use js::jsapi::{IsReadableStream, NewReadableDefaultStreamObject}; -use js::jsval::UndefinedValue; +use js::jsapi::{HandleObject, Heap, JSFunction, JSObject, JS_ValueToFunction}; +use js::jsapi::{ + IsReadableStream, NewReadableDefaultStreamObject, ReadableStreamCancel, ReadableStreamIsLocked, +}; +use js::jsval::{BooleanValue, UndefinedValue}; use js::rust::{Handle, IntoHandle}; use std::ptr; use std::rc::Rc; @@ -25,6 +30,36 @@ pub struct ReadableStream { stream: Heap<*mut JSObject>, } +impl ReadableStream { + /// + #[allow(non_snake_case)] + pub fn Constructor( + cx: SafeJSContext, + global: &GlobalScope, + underlying_source: *mut JSObject, + size: Rc, + high_watermark: Finite, + proto: *mut JSObject, + ) -> DomRoot { + let stream = + construct_default_readablestream(cx, underlying_source, size, high_watermark, proto); + + reflect_dom_object( + Box::new(ReadableStream { + reflector_: Reflector::new(), + stream, + }), + global, + Wrap, + ) + } + + pub fn Cancel(&self, reason: DOMString) -> Fallible> { + let cx = self.global().get_cx(); + cancel_readablestream(cx, &self.stream, reason) + } +} + #[allow(unsafe_code)] fn construct_default_readablestream( cx: SafeJSContext, @@ -62,27 +97,35 @@ fn construct_default_readablestream( heap } -impl ReadableStream { - /// - #[allow(non_snake_case)] - pub fn Constructor( - cx: SafeJSContext, - global: &GlobalScope, - underlying_source: *mut JSObject, - size: Rc, - high_watermark: Finite, - proto: *mut JSObject, - ) -> DomRoot { - let stream = - construct_default_readablestream(cx, underlying_source, size, high_watermark, proto); +#[allow(unsafe_code)] +fn cancel_readablestream( + cx: SafeJSContext, + stream: &Heap<*mut JSObject>, + reason: DOMString, +) -> Fallible> { + unsafe { + let stream = stream.handle(); - reflect_dom_object( - Box::new(ReadableStream { - reflector_: Reflector::new(), - stream, - }), - global, - Wrap, - ) + let mut is_locked = false; + let is_locked_ptr = &mut is_locked as *mut _; + ReadableStreamIsLocked(*cx, stream, is_locked_ptr); + + let is_stream = IsReadableStream(stream.get()); + + if !is_stream || is_locked { + return Err(Error::Type( + "The stream you are trying to cancel is not a ReadableStream, or it is locked." + .to_string(), + )); + } + + rooted!(in(*cx) let mut reason_val = UndefinedValue()); + (*reason).to_jsval(*cx, reason_val.handle_mut()); + + rooted!(in(*cx) let raw_cancel_promise = ReadableStreamCancel(*cx, stream, reason_val.handle().into_handle())); + + let cancel_promise = Promise::new_with_js_promise(raw_cancel_promise.handle(), cx); + + Ok(cancel_promise) } } diff --git a/components/script/dom/webidls/ReadableStream.webidl b/components/script/dom/webidls/ReadableStream.webidl index 7ea0ef5f23b7..719b0382c304 100644 --- a/components/script/dom/webidls/ReadableStream.webidl +++ b/components/script/dom/webidls/ReadableStream.webidl @@ -8,6 +8,7 @@ [Exposed=(Window,Worker)] interface ReadableStream { constructor(object underlyingSource, Function size, HighWatermark highWaterMark, object proto); + [Throws] Promise cancel(DOMString reason); }; typedef double HighWatermark; From 747098f244f8bead011498b93b67b27af630d501 Mon Sep 17 00:00:00 2001 From: Gregory Terzian Date: Sun, 23 Feb 2020 22:35:51 +0800 Subject: [PATCH 5/9] add getReader --- components/script/dom/readablestream.rs | 60 ++++++++++++++++--- .../script/dom/webidls/ReadableStream.webidl | 1 + 2 files changed, 52 insertions(+), 9 deletions(-) diff --git a/components/script/dom/readablestream.rs b/components/script/dom/readablestream.rs index 5509ece3a1ef..258954be328d 100644 --- a/components/script/dom/readablestream.rs +++ b/components/script/dom/readablestream.rs @@ -14,13 +14,15 @@ use crate::dom::promise::Promise; use crate::js::conversions::ToJSValConvertible; use crate::script_runtime::JSContext as SafeJSContext; use dom_struct::dom_struct; -use js::jsapi::{HandleObject, Heap, JSFunction, JSObject, JS_ValueToFunction}; +use js::jsapi::{Heap, JSObject, JS_ValueToFunction}; use js::jsapi::{ - IsReadableStream, NewReadableDefaultStreamObject, ReadableStreamCancel, ReadableStreamIsLocked, + IsReadableStream, NewReadableDefaultStreamObject, ReadableStreamCancel, + ReadableStreamGetReader, ReadableStreamIsLocked, + ReadableStreamReaderMode as JSReadableStreamReaderMode, }; -use js::jsval::{BooleanValue, UndefinedValue}; +use js::jsval::UndefinedValue; use js::rust::{Handle, IntoHandle}; -use std::ptr; +use std::ptr::NonNull; use std::rc::Rc; #[dom_struct] @@ -44,20 +46,60 @@ impl ReadableStream { let stream = construct_default_readablestream(cx, underlying_source, size, high_watermark, proto); + ReadableStream::new(global, stream) + } + + fn new_inherited(stream: Heap<*mut JSObject>) -> ReadableStream { + ReadableStream { + reflector_: Reflector::new(), + stream, + } + } + + fn new(global: &GlobalScope, stream: Heap<*mut JSObject>) -> DomRoot { reflect_dom_object( - Box::new(ReadableStream { - reflector_: Reflector::new(), - stream, - }), + Box::new(ReadableStream::new_inherited(stream)), global, Wrap, ) } +} - pub fn Cancel(&self, reason: DOMString) -> Fallible> { +impl ReadableStreamMethods for ReadableStream { + fn Cancel(&self, reason: DOMString) -> Fallible> { let cx = self.global().get_cx(); cancel_readablestream(cx, &self.stream, reason) } + + fn GetReader(&self, cx: SafeJSContext) -> Fallible> { + get_stream_reader(cx, &self.stream) + } +} + +#[allow(unsafe_code)] +fn get_stream_reader( + cx: SafeJSContext, + stream: &Heap<*mut JSObject>, +) -> Fallible> { + unsafe { + let stream = stream.handle(); + if !IsReadableStream(stream.get()) { + return Err(Error::Type( + "The stream you are trying to create a reader for is not a ReadableStream." + .to_string(), + )); + } + + // TODO: support ReadableStreamBYOBReader, + // SM currently seems to only support default mode, + // see https://github.com/servo/mozjs/blob/ + // bc4935b668171863e537d3fb0d911001a6742013/mozjs/js/public/Stream.h#L274 + Ok(NonNull::new_unchecked(ReadableStreamGetReader( + *cx, + stream, + JSReadableStreamReaderMode::Default, + ))) + } } #[allow(unsafe_code)] diff --git a/components/script/dom/webidls/ReadableStream.webidl b/components/script/dom/webidls/ReadableStream.webidl index 719b0382c304..a77bb5fa5b42 100644 --- a/components/script/dom/webidls/ReadableStream.webidl +++ b/components/script/dom/webidls/ReadableStream.webidl @@ -9,6 +9,7 @@ interface ReadableStream { constructor(object underlyingSource, Function size, HighWatermark highWaterMark, object proto); [Throws] Promise cancel(DOMString reason); + [Throws] object getReader(); }; typedef double HighWatermark; From d1b9bf4f9d2e0a5c7d3c07d4ee59c49c793c1847 Mon Sep 17 00:00:00 2001 From: Gregory Terzian Date: Sun, 23 Feb 2020 23:54:32 +0800 Subject: [PATCH 6/9] impl locked --- components/script/dom/readablestream.rs | 11 +++++++++++ components/script/dom/webidls/ReadableStream.webidl | 1 + 2 files changed, 12 insertions(+) diff --git a/components/script/dom/readablestream.rs b/components/script/dom/readablestream.rs index 258954be328d..9eb06fcf5e72 100644 --- a/components/script/dom/readablestream.rs +++ b/components/script/dom/readablestream.rs @@ -74,6 +74,17 @@ impl ReadableStreamMethods for ReadableStream { fn GetReader(&self, cx: SafeJSContext) -> Fallible> { get_stream_reader(cx, &self.stream) } + + #[allow(unsafe_code)] + fn Locked(&self) -> bool { + let mut is_locked = false; + unsafe { + let stream = self.stream.handle(); + let is_locked_ptr = &mut is_locked as *mut _; + ReadableStreamIsLocked(*self.global().get_cx(), stream, is_locked_ptr); + } + is_locked + } } #[allow(unsafe_code)] diff --git a/components/script/dom/webidls/ReadableStream.webidl b/components/script/dom/webidls/ReadableStream.webidl index a77bb5fa5b42..5f12ea6ed19e 100644 --- a/components/script/dom/webidls/ReadableStream.webidl +++ b/components/script/dom/webidls/ReadableStream.webidl @@ -10,6 +10,7 @@ interface ReadableStream { constructor(object underlyingSource, Function size, HighWatermark highWaterMark, object proto); [Throws] Promise cancel(DOMString reason); [Throws] object getReader(); + readonly attribute boolean locked; }; typedef double HighWatermark; From b8f63c3ab87ef5ad21abd9e3b8b59b16eb7285ad Mon Sep 17 00:00:00 2001 From: Gregory Terzian Date: Mon, 24 Feb 2020 01:08:29 +0800 Subject: [PATCH 7/9] impl tee --- components/script/dom/readablestream.rs | 65 ++++++++++++++----- .../script/dom/webidls/ReadableStream.webidl | 1 + 2 files changed, 49 insertions(+), 17 deletions(-) diff --git a/components/script/dom/readablestream.rs b/components/script/dom/readablestream.rs index 9eb06fcf5e72..8a77085ddb38 100644 --- a/components/script/dom/readablestream.rs +++ b/components/script/dom/readablestream.rs @@ -9,6 +9,7 @@ use crate::dom::bindings::num::Finite; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::DOMString; +use crate::dom::bindings::utils::to_frozen_array; use crate::dom::globalscope::GlobalScope; use crate::dom::promise::Promise; use crate::js::conversions::ToJSValConvertible; @@ -18,10 +19,10 @@ use js::jsapi::{Heap, JSObject, JS_ValueToFunction}; use js::jsapi::{ IsReadableStream, NewReadableDefaultStreamObject, ReadableStreamCancel, ReadableStreamGetReader, ReadableStreamIsLocked, - ReadableStreamReaderMode as JSReadableStreamReaderMode, + ReadableStreamReaderMode as JSReadableStreamReaderMode, ReadableStreamTee, }; -use js::jsval::UndefinedValue; -use js::rust::{Handle, IntoHandle}; +use js::jsval::{JSVal, UndefinedValue}; +use js::rust::{Handle, IntoHandle, IntoMutableHandle, MutableHandle}; use std::ptr::NonNull; use std::rc::Rc; @@ -75,15 +76,34 @@ impl ReadableStreamMethods for ReadableStream { get_stream_reader(cx, &self.stream) } - #[allow(unsafe_code)] fn Locked(&self) -> bool { - let mut is_locked = false; - unsafe { - let stream = self.stream.handle(); - let is_locked_ptr = &mut is_locked as *mut _; - ReadableStreamIsLocked(*self.global().get_cx(), stream, is_locked_ptr); + stream_is_locked(self.global().get_cx(), &self.stream) + } + + fn Tee(&self, cx: SafeJSContext) -> Fallible { + tee_stream(cx, &self.stream) + } +} + +#[allow(unsafe_code)] +fn tee_stream(cx: SafeJSContext, stream: &Heap<*mut JSObject>) -> Fallible { + unsafe { + rooted!(in(*cx) let mut branch1_stream = UndefinedValue()); + rooted!(in(*cx) let mut branch2_stream = UndefinedValue()); + + if !ReadableStreamTee( + *cx, + stream.handle(), + MutableHandle::new(&mut branch1_stream.to_object()).into_handle_mut(), + MutableHandle::new(&mut branch2_stream.to_object()).into_handle_mut(), + ) { + return Err(Error::Type( + "The stream you are trying to tee is not a ReadableStream, or it is locked." + .to_string(), + )); } - is_locked + + Ok(to_frozen_array(&[*branch1_stream, *branch2_stream], cx)) } } @@ -150,6 +170,17 @@ fn construct_default_readablestream( heap } +#[allow(unsafe_code)] +fn stream_is_locked(cx: SafeJSContext, stream: &Heap<*mut JSObject>) -> bool { + let mut is_locked = false; + unsafe { + let stream = stream.handle(); + let is_locked_ptr = &mut is_locked as *mut _; + ReadableStreamIsLocked(*cx, stream, is_locked_ptr); + } + is_locked +} + #[allow(unsafe_code)] fn cancel_readablestream( cx: SafeJSContext, @@ -157,14 +188,14 @@ fn cancel_readablestream( reason: DOMString, ) -> Fallible> { unsafe { - let stream = stream.handle(); - - let mut is_locked = false; - let is_locked_ptr = &mut is_locked as *mut _; - ReadableStreamIsLocked(*cx, stream, is_locked_ptr); - let is_stream = IsReadableStream(stream.get()); + let is_locked = if is_stream { + stream_is_locked(cx.clone(), stream) + } else { + false + }; + if !is_stream || is_locked { return Err(Error::Type( "The stream you are trying to cancel is not a ReadableStream, or it is locked." @@ -175,7 +206,7 @@ fn cancel_readablestream( rooted!(in(*cx) let mut reason_val = UndefinedValue()); (*reason).to_jsval(*cx, reason_val.handle_mut()); - rooted!(in(*cx) let raw_cancel_promise = ReadableStreamCancel(*cx, stream, reason_val.handle().into_handle())); + rooted!(in(*cx) let raw_cancel_promise = ReadableStreamCancel(*cx, stream.handle(), reason_val.handle().into_handle())); let cancel_promise = Promise::new_with_js_promise(raw_cancel_promise.handle(), cx); diff --git a/components/script/dom/webidls/ReadableStream.webidl b/components/script/dom/webidls/ReadableStream.webidl index 5f12ea6ed19e..77981978ccaa 100644 --- a/components/script/dom/webidls/ReadableStream.webidl +++ b/components/script/dom/webidls/ReadableStream.webidl @@ -10,6 +10,7 @@ interface ReadableStream { constructor(object underlyingSource, Function size, HighWatermark highWaterMark, object proto); [Throws] Promise cancel(DOMString reason); [Throws] object getReader(); + [Throws] /*FrozenArray*/any tee(); readonly attribute boolean locked; }; From 14835482f424777eeaceb1531022c2794fcb713f Mon Sep 17 00:00:00 2001 From: Gregory Terzian Date: Mon, 24 Feb 2020 01:33:56 +0800 Subject: [PATCH 8/9] cleanup --- components/script/dom/readablestream.rs | 9 +++++++-- components/script/dom/webidls/ReadableStream.webidl | 4 +--- python/tidy/servo_tidy/tidy.py | 1 + 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/components/script/dom/readablestream.rs b/components/script/dom/readablestream.rs index 8a77085ddb38..3bb058f82f9c 100644 --- a/components/script/dom/readablestream.rs +++ b/components/script/dom/readablestream.rs @@ -34,7 +34,7 @@ pub struct ReadableStream { } impl ReadableStream { - /// + /// #[allow(non_snake_case)] pub fn Constructor( cx: SafeJSContext, @@ -67,19 +67,23 @@ impl ReadableStream { } impl ReadableStreamMethods for ReadableStream { + /// fn Cancel(&self, reason: DOMString) -> Fallible> { let cx = self.global().get_cx(); cancel_readablestream(cx, &self.stream, reason) } + /// fn GetReader(&self, cx: SafeJSContext) -> Fallible> { get_stream_reader(cx, &self.stream) } + /// fn Locked(&self) -> bool { stream_is_locked(self.global().get_cx(), &self.stream) } + /// fn Tee(&self, cx: SafeJSContext) -> Fallible { tee_stream(cx, &self.stream) } @@ -206,7 +210,8 @@ fn cancel_readablestream( rooted!(in(*cx) let mut reason_val = UndefinedValue()); (*reason).to_jsval(*cx, reason_val.handle_mut()); - rooted!(in(*cx) let raw_cancel_promise = ReadableStreamCancel(*cx, stream.handle(), reason_val.handle().into_handle())); + rooted!(in(*cx) let raw_cancel_promise = + ReadableStreamCancel(*cx, stream.handle(), reason_val.handle().into_handle())); let cancel_promise = Promise::new_with_js_promise(raw_cancel_promise.handle(), cx); diff --git a/components/script/dom/webidls/ReadableStream.webidl b/components/script/dom/webidls/ReadableStream.webidl index 77981978ccaa..5d50fe4afaaa 100644 --- a/components/script/dom/webidls/ReadableStream.webidl +++ b/components/script/dom/webidls/ReadableStream.webidl @@ -1,10 +1,8 @@ /* 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/. */ -/* - * The origin of this IDL file is: https://streams.spec.whatwg.org/#rs-class - */ +// https://streams.spec.whatwg.org/#rs-class [Exposed=(Window,Worker)] interface ReadableStream { constructor(object underlyingSource, Function size, HighWatermark highWaterMark, object proto); diff --git a/python/tidy/servo_tidy/tidy.py b/python/tidy/servo_tidy/tidy.py index cbbb1a542d4a..83b3e253d72d 100644 --- a/python/tidy/servo_tidy/tidy.py +++ b/python/tidy/servo_tidy/tidy.py @@ -103,6 +103,7 @@ def wpt_path(*args): b"//immersive-web.github.io/", b"//github.com/immersive-web/webxr-test-api/", b"//gpuweb.github.io", + b"//streams.spec.whatwg.org", # Not a URL b"// This interface is entirely internal to Servo, and should not be" + b" accessible to\n// web pages." From 1d7fdc69c2162fabc77c1dea5674d6918cbfe53c Mon Sep 17 00:00:00 2001 From: Gregory Terzian Date: Mon, 24 Feb 2020 16:08:56 +0800 Subject: [PATCH 9/9] use optional object for queueing strat, turn-on tests --- components/script/dom/readablestream.rs | 84 ++++++++++++------- .../script/dom/webidls/ReadableStream.webidl | 3 +- tests/wpt/include.ini | 12 +++ 3 files changed, 65 insertions(+), 34 deletions(-) diff --git a/components/script/dom/readablestream.rs b/components/script/dom/readablestream.rs index 3bb058f82f9c..8a06070103fc 100644 --- a/components/script/dom/readablestream.rs +++ b/components/script/dom/readablestream.rs @@ -2,10 +2,8 @@ * 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 crate::dom::bindings::codegen::Bindings::FunctionBinding::Function; use crate::dom::bindings::codegen::Bindings::ReadableStreamBinding::{ReadableStreamMethods, Wrap}; use crate::dom::bindings::error::{Error, Fallible}; -use crate::dom::bindings::num::Finite; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::DOMString; @@ -15,15 +13,16 @@ use crate::dom::promise::Promise; use crate::js::conversions::ToJSValConvertible; use crate::script_runtime::JSContext as SafeJSContext; use dom_struct::dom_struct; -use js::jsapi::{Heap, JSObject, JS_ValueToFunction}; +use js::jsapi::{Heap, JSFunction, JSObject, JS_ValueToFunction}; use js::jsapi::{ - IsReadableStream, NewReadableDefaultStreamObject, ReadableStreamCancel, + IsReadableStream, JS_GetProperty, NewReadableDefaultStreamObject, ReadableStreamCancel, ReadableStreamGetReader, ReadableStreamIsLocked, ReadableStreamReaderMode as JSReadableStreamReaderMode, ReadableStreamTee, }; use js::jsval::{JSVal, UndefinedValue}; use js::rust::{Handle, IntoHandle, IntoMutableHandle, MutableHandle}; -use std::ptr::NonNull; +use std::ffi::CString; +use std::ptr::{self, NonNull}; use std::rc::Rc; #[dom_struct] @@ -40,12 +39,9 @@ impl ReadableStream { cx: SafeJSContext, global: &GlobalScope, underlying_source: *mut JSObject, - size: Rc, - high_watermark: Finite, - proto: *mut JSObject, + queuing_strategy: Option<*mut JSObject>, ) -> DomRoot { - let stream = - construct_default_readablestream(cx, underlying_source, size, high_watermark, proto); + let stream = construct_default_readablestream(cx, underlying_source, queuing_strategy); ReadableStream::new(global, stream) } @@ -141,34 +137,58 @@ fn get_stream_reader( fn construct_default_readablestream( cx: SafeJSContext, underlying_source: *mut JSObject, - size: Rc, - high_watermark: Finite, - proto: *mut JSObject, + queuing_strategy: Option<*mut JSObject>, ) -> Heap<*mut JSObject> { let heap = Heap::default(); let source = Handle::new(&underlying_source); - let proto = Handle::new(&proto); unsafe { - rooted!(in(*cx) let mut size_val = UndefinedValue()); - size.to_jsval(*cx, size_val.handle_mut()); - - let func = JS_ValueToFunction(*cx, size_val.handle().into_handle()); - let size_func = Handle::new(&func); - - rooted!(in(*cx) - let stream = NewReadableDefaultStreamObject( + if let Some(object) = queuing_strategy.as_ref() { + rooted!(in(*cx) let mut high_watermark = UndefinedValue()); + let name = CString::new("highWaterMark").unwrap(); + JS_GetProperty( *cx, - source.into_handle(), - size_func.into_handle(), - *high_watermark, - proto.into_handle() - ) - ); - - assert!(IsReadableStream(stream.get())); - - heap.set(stream.get()); + Handle::new(object).into_handle(), + name.as_ptr(), + high_watermark.handle_mut().into_handle_mut(), + ); + + rooted!(in(*cx) let mut size = UndefinedValue()); + let name = CString::new("size").unwrap(); + JS_GetProperty( + *cx, + Handle::new(object).into_handle(), + name.as_ptr(), + size.handle_mut().into_handle_mut(), + ); + let func = JS_ValueToFunction(*cx, size.handle().into_handle()); + + rooted!(in(*cx) + let stream = NewReadableDefaultStreamObject( + *cx, + source.into_handle(), + Handle::new(&func).into_handle(), + high_watermark.get().to_double(), + Handle::new(&ptr::null_mut::()).into_handle(), + ) + ); + + assert!(IsReadableStream(stream.get())); + heap.set(stream.get()); + } else { + rooted!(in(*cx) + let stream = NewReadableDefaultStreamObject( + *cx, + source.into_handle(), + Handle::new(&ptr::null_mut::()).into_handle(), + 1.0, + Handle::new(&ptr::null_mut::()).into_handle(), + ) + ); + + assert!(IsReadableStream(stream.get())); + heap.set(stream.get()); + } } heap diff --git a/components/script/dom/webidls/ReadableStream.webidl b/components/script/dom/webidls/ReadableStream.webidl index 5d50fe4afaaa..6340aa167301 100644 --- a/components/script/dom/webidls/ReadableStream.webidl +++ b/components/script/dom/webidls/ReadableStream.webidl @@ -5,11 +5,10 @@ // https://streams.spec.whatwg.org/#rs-class [Exposed=(Window,Worker)] interface ReadableStream { - constructor(object underlyingSource, Function size, HighWatermark highWaterMark, object proto); + constructor(object underlyingSource, optional object queuingStrategy); [Throws] Promise cancel(DOMString reason); [Throws] object getReader(); [Throws] /*FrozenArray*/any tee(); readonly attribute boolean locked; }; -typedef double HighWatermark; diff --git a/tests/wpt/include.ini b/tests/wpt/include.ini index 80103c16bf6f..5bcb674d9d7d 100644 --- a/tests/wpt/include.ini +++ b/tests/wpt/include.ini @@ -157,6 +157,18 @@ skip: true skip: false [selection] skip: false +[streams] + skip: false + [piping] + skip: true + [readable-byte-streams] + skip: true + [readable-streams] + skip: false + [transform-streams] + skip: true + [writable-streams] + skip: true [subresource-integrity] skip: false [touch-events]