Skip to main content

script/dom/bindings/
buffer_source.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5#![expect(unsafe_code)]
6
7use std::marker::PhantomData;
8use std::ptr;
9
10use js::context::{JSContext, NoGC};
11use js::jsapi::{
12    GetArrayBufferByteLength, Heap, IsArrayBufferObject, IsDetachedArrayBufferObject,
13    JS_GetArrayBufferViewByteLength, JS_GetArrayBufferViewByteOffset, JS_GetArrayBufferViewType,
14    JS_GetTypedArrayLength, JS_IsArrayBufferViewObject, JS_IsTypedArrayObject, JSObject, Type,
15};
16use js::jsval::{ObjectValue, UndefinedValue};
17use js::rust::wrappers2::{
18    ArrayBufferClone, ArrayBufferCopyData, DetachArrayBuffer, HasDefinedArrayBufferDetachKey,
19    JS_ClearPendingException, JS_GetArrayBufferViewBuffer, JS_GetPendingException,
20    JS_NewBigInt64ArrayWithBuffer, JS_NewBigUint64ArrayWithBuffer, JS_NewDataView,
21    JS_NewFloat16ArrayWithBuffer, JS_NewFloat32ArrayWithBuffer, JS_NewFloat64ArrayWithBuffer,
22    JS_NewInt8ArrayWithBuffer, JS_NewInt16ArrayWithBuffer, JS_NewInt32ArrayWithBuffer,
23    JS_NewUint8ArrayWithBuffer, JS_NewUint8ClampedArrayWithBuffer, JS_NewUint16ArrayWithBuffer,
24    JS_NewUint32ArrayWithBuffer, NewArrayBuffer, NewArrayBufferWithContents,
25    StealArrayBufferContents,
26};
27use js::rust::{
28    CustomAutoRooterGuard, Handle, MutableHandleObject,
29    MutableHandleValue as SafeMutableHandleValue,
30};
31use js::typedarray::{
32    ArrayBufferU8, ArrayBufferViewU8, CreateWith, TypedArray, TypedArrayElement,
33    TypedArrayElementCreator,
34};
35
36use crate::dom::bindings::codegen::UnionTypes::ArrayBufferViewOrArrayBuffer;
37use crate::dom::bindings::error::{Error, Fallible};
38use crate::dom::bindings::trace::RootedTraceableBox;
39
40pub(crate) type RootedTypedArray<T> = RootedTraceableBox<TypedArray<T, Box<Heap<*mut JSObject>>>>;
41
42/// Represents a `BufferSource` as defined in the WebIDL specification.
43///
44/// A `BufferSource` is either an `ArrayBuffer` or an `ArrayBufferView`, which
45/// provides a view onto an `ArrayBuffer`.
46///
47/// See: <https://webidl.spec.whatwg.org/#BufferSource>
48#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
49pub(crate) enum BufferSource {
50    /// Represents an `ArrayBufferView` (e.g., `Uint8Array`, `DataView`).
51    /// See: <https://webidl.spec.whatwg.org/#ArrayBufferView>
52    ArrayBufferView(Box<Heap<*mut JSObject>>),
53
54    /// Represents an `ArrayBuffer`, a fixed-length binary data buffer.
55    /// See: <https://webidl.spec.whatwg.org/#idl-ArrayBuffer>
56    ArrayBuffer(Box<Heap<*mut JSObject>>),
57}
58
59impl Clone for BufferSource {
60    fn clone(&self) -> Self {
61        match self {
62            BufferSource::ArrayBufferView(heap) => {
63                BufferSource::ArrayBufferView(Heap::boxed(heap.get()))
64            },
65            BufferSource::ArrayBuffer(heap) => BufferSource::ArrayBuffer(Heap::boxed(heap.get())),
66        }
67    }
68}
69
70pub(crate) enum ArrayBufferViewOrArrayBufferRef<'a> {
71    ArrayBufferView(&'a RootedTypedArray<ArrayBufferViewU8>),
72    ArrayBuffer(&'a RootedTypedArray<ArrayBufferU8>),
73}
74
75impl<'a> From<&'a RootedTypedArray<ArrayBufferViewU8>> for ArrayBufferViewOrArrayBufferRef<'a> {
76    fn from(view: &'a RootedTypedArray<ArrayBufferViewU8>) -> Self {
77        ArrayBufferViewOrArrayBufferRef::ArrayBufferView(view)
78    }
79}
80
81impl<'a> From<&'a RootedTypedArray<ArrayBufferU8>> for ArrayBufferViewOrArrayBufferRef<'a> {
82    fn from(buffer: &'a RootedTypedArray<ArrayBufferU8>) -> Self {
83        ArrayBufferViewOrArrayBufferRef::ArrayBuffer(buffer)
84    }
85}
86
87impl<'a> From<&'a ArrayBufferViewOrArrayBuffer> for ArrayBufferViewOrArrayBufferRef<'a> {
88    fn from(view_or_buffer: &'a ArrayBufferViewOrArrayBuffer) -> Self {
89        match view_or_buffer {
90            ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => {
91                ArrayBufferViewOrArrayBufferRef::ArrayBufferView(view)
92            },
93            ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => {
94                ArrayBufferViewOrArrayBufferRef::ArrayBuffer(buffer)
95            },
96        }
97    }
98}
99
100/// <https://webidl.spec.whatwg.org/#dfn-get-buffer-source-copy>
101///
102/// Spec steps and how they're covered:
103///
104/// - **Convert to JS value**: Handled by WebIDL bindings before this function
105///   receives the typed `ArrayBufferViewOrArrayBuffer` union.
106///
107/// - **ArrayBufferView offset/length**: `view.to_vec()` (mozjs) calls
108///   `GetArrayBufferViewLengthAndData`, which respects the view's
109///   `[[ByteOffset]]` and `[[ByteLength]]` — only the viewed bytes are copied.
110///
111/// - **ArrayBuffer**: `buffer.to_vec()` (mozjs) calls
112///   `GetArrayBufferLengthAndData`, copying the entire buffer contents.
113///
114/// - **Detached buffer**: When a buffer is detached, SpiderMonkey's
115///   `GetArrayBuffer(LengthAndData|ViewLengthAndData)` returns a null pointer
116///   and zero length. `to_vec()` thus produces an empty `Vec<u8>`.
117///
118/// - **SharedArrayBuffer**: Not applicable — `ArrayBufferViewOrArrayBuffer`
119///   does not include a SharedArrayBuffer variant.
120pub(crate) fn get_buffer_source_copy(source: ArrayBufferViewOrArrayBufferRef<'_>) -> Vec<u8> {
121    match source {
122        ArrayBufferViewOrArrayBufferRef::ArrayBufferView(view) => view.to_vec(),
123        ArrayBufferViewOrArrayBufferRef::ArrayBuffer(buffer) => buffer.to_vec(),
124    }
125    .unwrap_or(vec![])
126}
127
128/// Returns a slice referencing the bytes in the buffer source, without copying.
129///
130/// Use this instead of [`get_buffer_source_copy`] when the data is consumed
131/// synchronously — it avoids the allocation of a `Vec<u8>`.
132pub(crate) fn get_buffer_source_slice<'a>(
133    source: &'a ArrayBufferViewOrArrayBuffer,
134    no_gc: &'a NoGC,
135) -> &'a [u8] {
136    match source {
137        ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.as_slice_safe(no_gc),
138        ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.as_slice_safe(no_gc),
139    }
140    .unwrap_or(&[])
141}
142
143pub(crate) fn create_heap_buffer_source_with_length<T>(
144    cx: &mut JSContext,
145    len: u32,
146) -> Fallible<RootedTraceableBox<HeapBufferSource<T>>>
147where
148    T: TypedArrayElement + TypedArrayElementCreator + 'static,
149    T::Element: Clone + Copy,
150{
151    rooted!(&in(cx) let mut array = ptr::null_mut::<JSObject>());
152    let typed_array_result =
153        create_buffer_source_with_length::<T>(cx, len as usize, array.handle_mut());
154    if typed_array_result.is_err() {
155        return Err(Error::JSFailed);
156    }
157
158    Ok(RootedTraceableBox::new(HeapBufferSource::<T>::new(
159        array.handle(),
160    )))
161}
162
163#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
164pub(crate) struct HeapBufferSource<T> {
165    buffer_source: BufferSource,
166    phantom: PhantomData<T>,
167}
168
169impl<T> Eq for HeapBufferSource<T> where T: TypedArrayElement {}
170
171impl<T> PartialEq for HeapBufferSource<T>
172where
173    T: TypedArrayElement,
174{
175    fn eq(&self, other: &Self) -> bool {
176        match &self.buffer_source {
177            BufferSource::ArrayBufferView(heap) | BufferSource::ArrayBuffer(heap) => match &other
178                .buffer_source
179            {
180                BufferSource::ArrayBufferView(from_heap) | BufferSource::ArrayBuffer(from_heap) => {
181                    std::ptr::eq(heap.get(), from_heap.get())
182                },
183            },
184        }
185    }
186}
187
188impl<T> Clone for HeapBufferSource<T>
189where
190    T: TypedArrayElement,
191{
192    fn clone(&self) -> Self {
193        HeapBufferSource {
194            buffer_source: self.buffer_source.clone(),
195            phantom: PhantomData,
196        }
197    }
198}
199
200impl<T> HeapBufferSource<T>
201where
202    T: TypedArrayElement,
203{
204    /// Create a buffer source from a rooted ArrayBuffer or ArrayBufferView.
205    pub(crate) fn new(object: Handle<*mut JSObject>) -> HeapBufferSource<T> {
206        let object = object.get();
207        assert!(!object.is_null());
208
209        HeapBufferSource {
210            buffer_source: if unsafe { IsArrayBufferObject(object) } {
211                BufferSource::ArrayBuffer(Heap::boxed(object))
212            } else {
213                assert!(unsafe { JS_IsArrayBufferViewObject(object) });
214                BufferSource::ArrayBufferView(Heap::boxed(object))
215            },
216            phantom: PhantomData,
217        }
218    }
219
220    pub(crate) fn from_view(
221        cx: &mut JSContext,
222        chunk: CustomAutoRooterGuard<TypedArray<T, *mut JSObject>>,
223    ) -> RootedTraceableBox<HeapBufferSource<T>> {
224        rooted!(&in(cx) let object = unsafe { *chunk.underlying_object() });
225        RootedTraceableBox::new(HeapBufferSource::<T>::new(object.handle()))
226    }
227
228    pub(crate) fn default() -> Self {
229        HeapBufferSource {
230            buffer_source: BufferSource::ArrayBufferView(Heap::boxed(std::ptr::null_mut())),
231            phantom: PhantomData,
232        }
233    }
234
235    pub(crate) fn is_initialized(&self) -> bool {
236        match &self.buffer_source {
237            BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer) => {
238                !buffer.get().is_null()
239            },
240        }
241    }
242
243    pub(crate) fn get_typed_array(&self) -> Result<RootedTypedArray<T>, ()> {
244        TypedArray::from(match &self.buffer_source {
245            BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer) => {
246                buffer.get()
247            },
248        })
249        .map(RootedTraceableBox::new)
250    }
251
252    pub(crate) fn get_buffer_view_value(
253        &self,
254        cx: &mut JSContext,
255        mut handle_mut: SafeMutableHandleValue,
256    ) {
257        match &self.buffer_source {
258            BufferSource::ArrayBufferView(buffer) => {
259                rooted!(&in(cx) let value = ObjectValue(buffer.get()));
260                handle_mut.set(*value);
261            },
262            BufferSource::ArrayBuffer(_) => {
263                unreachable!("BufferSource::ArrayBuffer does not have a view buffer.")
264            },
265        }
266    }
267
268    pub(crate) fn get_array_buffer_view_buffer(
269        &self,
270        cx: &mut JSContext,
271    ) -> RootedTraceableBox<HeapBufferSource<ArrayBufferU8>> {
272        match &self.buffer_source {
273            BufferSource::ArrayBufferView(buffer) => unsafe {
274                let mut is_shared = false;
275                rooted!(&in(cx) let view_buffer =
276                         JS_GetArrayBufferViewBuffer(cx, Handle::from_raw(buffer.handle()), &mut is_shared));
277
278                RootedTraceableBox::new(HeapBufferSource::<ArrayBufferU8>::new(
279                    view_buffer.handle(),
280                ))
281            },
282            BufferSource::ArrayBuffer(_) => {
283                unreachable!("BufferSource::ArrayBuffer does not have a view buffer.")
284            },
285        }
286    }
287
288    /// <https://tc39.es/ecma262/#sec-detacharraybuffer>
289    pub(crate) fn detach_buffer(&self, cx: &mut JSContext) -> bool {
290        assert!(self.is_initialized());
291        match &self.buffer_source {
292            BufferSource::ArrayBufferView(buffer) => {
293                let mut is_shared = false;
294                unsafe {
295                    // assert buffer is an ArrayBuffer view
296                    assert!(JS_IsArrayBufferViewObject(*buffer.handle()));
297                    rooted!(&in(cx) let view_buffer =
298                            JS_GetArrayBufferViewBuffer(cx, Handle::from_raw(buffer.handle()), &mut is_shared));
299                    // This buffer is always created unshared
300                    debug_assert!(!is_shared);
301                    // Detach the ArrayBuffer
302                    DetachArrayBuffer(cx, view_buffer.handle())
303                }
304            },
305            BufferSource::ArrayBuffer(buffer) => unsafe {
306                DetachArrayBuffer(cx, Handle::from_raw(buffer.handle()))
307            },
308        }
309    }
310
311    pub(crate) fn typed_array_to_option(&self) -> Option<RootedTypedArray<T>> {
312        if self.is_initialized() {
313            self.get_typed_array().ok()
314        } else {
315            warn!("Buffer not initialized.");
316            None
317        }
318    }
319
320    pub(crate) fn is_detached_buffer(&self, cx: &mut JSContext) -> bool {
321        assert!(self.is_initialized());
322        match &self.buffer_source {
323            BufferSource::ArrayBufferView(buffer) => {
324                let mut is_shared = false;
325                unsafe {
326                    assert!(JS_IsArrayBufferViewObject(*buffer.handle()));
327                    rooted!(&in(cx) let view_buffer =
328                            JS_GetArrayBufferViewBuffer(cx, Handle::from_raw(buffer.handle()), &mut is_shared));
329                    debug_assert!(!is_shared);
330                    IsDetachedArrayBufferObject(*view_buffer.handle())
331                }
332            },
333            BufferSource::ArrayBuffer(buffer) => unsafe {
334                IsDetachedArrayBufferObject(*buffer.handle())
335            },
336        }
337    }
338
339    pub(crate) fn viewed_buffer_array_byte_length(&self, cx: &mut JSContext) -> usize {
340        assert!(self.is_initialized());
341        match &self.buffer_source {
342            BufferSource::ArrayBufferView(buffer) => {
343                let mut is_shared = false;
344                unsafe {
345                    assert!(JS_IsArrayBufferViewObject(*buffer.handle()));
346                    rooted!(&in(cx) let view_buffer =
347                            JS_GetArrayBufferViewBuffer(cx, Handle::from_raw(buffer.handle()), &mut is_shared));
348                    debug_assert!(!is_shared);
349                    GetArrayBufferByteLength(*view_buffer.handle())
350                }
351            },
352            BufferSource::ArrayBuffer(buffer) => unsafe {
353                GetArrayBufferByteLength(*buffer.handle())
354            },
355        }
356    }
357
358    pub(crate) fn byte_length(&self) -> usize {
359        match &self.buffer_source {
360            BufferSource::ArrayBufferView(buffer) => unsafe {
361                JS_GetArrayBufferViewByteLength(*buffer.handle())
362            },
363            BufferSource::ArrayBuffer(buffer) => unsafe {
364                GetArrayBufferByteLength(*buffer.handle())
365            },
366        }
367    }
368
369    pub(crate) fn get_byte_offset(&self) -> usize {
370        match &self.buffer_source {
371            BufferSource::ArrayBufferView(buffer) => unsafe {
372                JS_GetArrayBufferViewByteOffset(*buffer.handle())
373            },
374            BufferSource::ArrayBuffer(_) => {
375                unreachable!("BufferSource::ArrayBuffer does not have a byte offset.")
376            },
377        }
378    }
379
380    pub(crate) fn get_typed_array_length(&self) -> usize {
381        match &self.buffer_source {
382            BufferSource::ArrayBufferView(buffer) => unsafe {
383                JS_GetTypedArrayLength(*buffer.handle())
384            },
385            BufferSource::ArrayBuffer(_) => {
386                unreachable!("BufferSource::ArrayBuffer does not have a length.")
387            },
388        }
389    }
390
391    /// <https://tc39.es/ecma262/#typedarray>
392    pub(crate) fn has_typed_array_name(&self) -> bool {
393        match &self.buffer_source {
394            BufferSource::ArrayBufferView(buffer) => unsafe {
395                JS_IsTypedArrayObject(*buffer.handle())
396            },
397            BufferSource::ArrayBuffer(_) => false,
398        }
399    }
400
401    pub(crate) fn get_array_buffer_view_type(&self) -> Type {
402        match &self.buffer_source {
403            BufferSource::ArrayBufferView(buffer) => unsafe {
404                JS_GetArrayBufferViewType(*buffer.handle())
405            },
406            BufferSource::ArrayBuffer(_) => unreachable!("ArrayBuffer does not have a view type."),
407        }
408    }
409
410    pub(crate) fn is_array_buffer_object(&self) -> bool {
411        match &self.buffer_source {
412            BufferSource::ArrayBufferView(heap) | BufferSource::ArrayBuffer(heap) => unsafe {
413                IsArrayBufferObject(*heap.handle())
414            },
415        }
416    }
417
418    /// <https://tc39.es/ecma262/#sec-clonearraybuffer>
419    pub(crate) fn clone_array_buffer(
420        &self,
421        cx: &mut JSContext,
422        byte_offset: usize,
423        byte_length: usize,
424    ) -> Fallible<RootedTraceableBox<HeapBufferSource<ArrayBufferU8>>> {
425        let result = match &self.buffer_source {
426            BufferSource::ArrayBufferView(buffer) => {
427                let mut is_shared = false;
428                rooted!(&in(cx) let view_buffer =
429                    unsafe { JS_GetArrayBufferViewBuffer(cx, Handle::from_raw(buffer.handle()), &mut is_shared) });
430                debug_assert!(!is_shared);
431
432                unsafe { ArrayBufferClone(cx, view_buffer.handle(), byte_offset, byte_length) }
433            },
434            BufferSource::ArrayBuffer(buffer) => unsafe {
435                ArrayBufferClone(
436                    cx,
437                    Handle::from_raw(buffer.handle()),
438                    byte_offset,
439                    byte_length,
440                )
441            },
442        };
443
444        rooted!(&in(cx) let result = result);
445
446        if result.is_null() {
447            // Normalize SpiderMonkey failure: consume pending exception and
448            // map it to a DOM Error.
449            rooted!(&in(cx) let mut _ex = UndefinedValue());
450            unsafe {
451                // If SpiderMonkey set an exception, clear it so callers see a clean cx.
452                if JS_GetPendingException(cx, _ex.handle_mut()) {
453                    JS_ClearPendingException(cx);
454                }
455            }
456
457            Err(Error::Type(c"can't clone array buffer".to_owned()))
458        } else {
459            Ok(RootedTraceableBox::new(
460                HeapBufferSource::<ArrayBufferU8>::new(result.handle()),
461            ))
462        }
463    }
464    /// <https://streams.spec.whatwg.org/#abstract-opdef-cloneasuint8array>
465    #[expect(unsafe_code)]
466    pub(crate) fn clone_as_uint8_array(
467        &self,
468        cx: &mut JSContext,
469    ) -> Fallible<RootedTraceableBox<HeapBufferSource<ArrayBufferViewU8>>> {
470        match &self.buffer_source {
471            BufferSource::ArrayBufferView(buffer) => {
472                // Assert: O is an Object.
473                // Assert: O has an [[ViewedArrayBuffer]] internal slot.
474                assert!(unsafe { JS_IsArrayBufferViewObject(*buffer.handle()) });
475
476                // Assert: ! IsDetachedBuffer(O.[[ViewedArrayBuffer]]) is false.
477                assert!(!self.is_detached_buffer(cx));
478
479                // Let buffer be ? CloneArrayBuffer(O.[[ViewedArrayBuffer]],
480                // O.[[ByteOffset]], O.[[ByteLength]], %ArrayBuffer%).
481                let byte_offset = self.get_byte_offset();
482                let byte_length = self.byte_length();
483
484                let buffer = self.clone_array_buffer(cx, byte_offset, byte_length)?;
485
486                // Let array be ! Construct(%Uint8Array%, « buffer »).
487                // Return array.
488                construct_typed_array(cx, &Type::Uint8, &buffer, 0, byte_length as i64)
489            },
490            BufferSource::ArrayBuffer(_buffer) => {
491                unreachable!("BufferSource::ArrayBuffer does not have a view buffer.")
492            },
493        }
494    }
495
496    pub(crate) fn is_undefined(&self) -> bool {
497        match &self.buffer_source {
498            BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer) => {
499                buffer.get().is_null()
500            },
501        }
502    }
503}
504
505impl<T> HeapBufferSource<T>
506where
507    T: TypedArrayElement + TypedArrayElementCreator + 'static,
508    T::Element: Clone + Copy,
509{
510    pub(crate) fn acquire_data(&self, cx: &mut JSContext) -> Result<Vec<T::Element>, ()> {
511        assert!(self.is_initialized());
512
513        typedarray!(&in(cx) let array: TypedArray = match &self.buffer_source {
514            BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer)
515            => {
516                buffer.get()
517            },
518        });
519        let data = if let Ok(array) =
520            array as Result<CustomAutoRooterGuard<'_, TypedArray<T, *mut JSObject>>, &mut ()>
521        {
522            if let Some(data) = array.to_vec() {
523                let _ = self.detach_buffer(cx);
524                Ok(data)
525            } else {
526                Err(())
527            }
528        } else {
529            Err(())
530        };
531
532        match &self.buffer_source {
533            BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer) => {
534                buffer.set(ptr::null_mut());
535            },
536        }
537        data
538    }
539
540    pub(crate) fn copy_data_to(
541        &self,
542        cx: &mut JSContext,
543        dest: &mut [T::Element],
544        source_start: usize,
545        source_end: usize,
546    ) -> Result<(), ()> {
547        assert!(self.is_initialized());
548        typedarray!(&in(cx) let array: TypedArray = match &self.buffer_source {
549            BufferSource::ArrayBufferView(buffer) |  BufferSource::ArrayBuffer(buffer)
550            => {
551                buffer.get()
552            },
553        });
554        let Ok(array) =
555            array as Result<CustomAutoRooterGuard<'_, TypedArray<T, *mut JSObject>>, &mut ()>
556        else {
557            return Err(());
558        };
559        let slice = (*array).as_slice_safe(cx.no_gc()).unwrap_or(&[]);
560        dest.copy_from_slice(&slice[source_start..source_end]);
561        Ok(())
562    }
563
564    pub(crate) fn copy_data_from(
565        &self,
566        cx: &mut JSContext,
567        source: CustomAutoRooterGuard<TypedArray<T, *mut JSObject>>,
568        dest_start: usize,
569        length: usize,
570    ) -> Result<(), ()> {
571        assert!(self.is_initialized());
572        typedarray!(&in(cx) let mut array: TypedArray = match &self.buffer_source {
573            BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer)
574            => {
575                buffer.get()
576            },
577        });
578        let Ok(mut array) =
579            array as Result<CustomAutoRooterGuard<'_, TypedArray<T, *mut JSObject>>, &mut ()>
580        else {
581            return Err(());
582        };
583        let slice = (*array).as_mut_slice_safe(cx.no_gc()).unwrap_or(&mut []);
584        let (_, dest) = slice.split_at_mut(dest_start);
585        let source = source.as_slice_safe(cx.no_gc()).unwrap_or(&[]);
586        dest[0..length].copy_from_slice(&source[0..length]);
587        Ok(())
588    }
589
590    pub(crate) fn set_data(&self, cx: &mut JSContext, data: &[T::Element]) -> Result<(), ()> {
591        rooted!(&in(cx) let mut array = ptr::null_mut::<JSObject>());
592        let _ = create_buffer_source::<T>(cx, data, array.handle_mut())?;
593
594        match &self.buffer_source {
595            BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer) => {
596                buffer.set(*array);
597            },
598        }
599        Ok(())
600    }
601
602    /// <https://streams.spec.whatwg.org/#abstract-opdef-cancopydatablockbytes>
603    // CanCopyDataBlockBytes(descriptorBuffer, destStart, queueBuffer, queueByteOffset, bytesToCopy)
604    pub(crate) fn can_copy_data_block_bytes(
605        &self,
606        cx: &mut JSContext,
607        to_index: usize,
608        from_buffer: &HeapBufferSource<ArrayBufferU8>,
609        from_index: usize,
610        bytes_to_copy: usize,
611    ) -> bool {
612        // Assert: toBuffer is an Object.
613        // Assert: toBuffer has an [[ArrayBufferData]] internal slot.
614        assert!(self.is_array_buffer_object());
615
616        // Assert: fromBuffer is an Object.
617        // Assert: fromBuffer has an [[ArrayBufferData]] internal slot.
618        assert!(from_buffer.is_array_buffer_object());
619
620        // If toBuffer is fromBuffer, return false.
621        match &self.buffer_source {
622            BufferSource::ArrayBufferView(heap) | BufferSource::ArrayBuffer(heap) => {
623                match &from_buffer.buffer_source {
624                    BufferSource::ArrayBufferView(from_heap) |
625                    BufferSource::ArrayBuffer(from_heap) => {
626                        if std::ptr::eq(heap.get(), from_heap.get()) {
627                            return false;
628                        }
629                    },
630                }
631            },
632        }
633
634        // If ! IsDetachedBuffer(toBuffer) is true, return false.
635        if self.is_detached_buffer(cx) {
636            return false;
637        }
638
639        // If ! IsDetachedBuffer(fromBuffer) is true, return false.
640        if from_buffer.is_detached_buffer(cx) {
641            return false;
642        }
643
644        // If toIndex + count > toBuffer.[[ArrayBufferByteLength]], return false.
645        if to_index + bytes_to_copy > self.byte_length() {
646            return false;
647        }
648
649        // If fromIndex + count > fromBuffer.[[ArrayBufferByteLength]], return false.
650        if from_index + bytes_to_copy > from_buffer.byte_length() {
651            return false;
652        }
653
654        // Return true.
655        true
656    }
657
658    pub(crate) fn copy_data_block_bytes(
659        &self,
660        cx: &mut JSContext,
661        dest_start: usize,
662        from_buffer: &HeapBufferSource<ArrayBufferU8>,
663        from_byte_offset: usize,
664        bytes_to_copy: usize,
665    ) -> bool {
666        match &self.buffer_source {
667            BufferSource::ArrayBufferView(heap) | BufferSource::ArrayBuffer(heap) => unsafe {
668                match &from_buffer.buffer_source {
669                    BufferSource::ArrayBufferView(from_heap) |
670                    BufferSource::ArrayBuffer(from_heap) => ArrayBufferCopyData(
671                        cx,
672                        Handle::from_raw(heap.handle()),
673                        dest_start,
674                        Handle::from_raw(from_heap.handle()),
675                        from_byte_offset,
676                        bytes_to_copy,
677                    ),
678                }
679            },
680        }
681    }
682
683    /// <https://streams.spec.whatwg.org/#can-transfer-array-buffer>
684    pub(crate) fn can_transfer_array_buffer(&self, cx: &mut JSContext) -> bool {
685        // Assert: O is an Object.
686        // Assert: O has an [[ArrayBufferData]] internal slot.
687        assert!(self.is_array_buffer_object());
688
689        // If ! IsDetachedBuffer(O) is true, return false.
690        if self.is_detached_buffer(cx) {
691            return false;
692        }
693
694        // If SameValue(O.[[ArrayBufferDetachKey]], undefined) is false, return false.
695        // Return true.
696        let mut is_defined = false;
697        match &self.buffer_source {
698            BufferSource::ArrayBufferView(heap) | BufferSource::ArrayBuffer(heap) => unsafe {
699                if !HasDefinedArrayBufferDetachKey(
700                    cx,
701                    Handle::from_raw(heap.handle()),
702                    &mut is_defined,
703                ) {
704                    return false;
705                }
706            },
707        }
708
709        !is_defined
710    }
711
712    /// <https://streams.spec.whatwg.org/#transfer-array-buffer>
713    pub(crate) fn transfer_array_buffer(
714        &self,
715        cx: &mut JSContext,
716    ) -> Fallible<RootedTraceableBox<HeapBufferSource<ArrayBufferU8>>> {
717        assert!(self.is_array_buffer_object());
718
719        // Assert: ! IsDetachedBuffer(O) is false.
720        assert!(!self.is_detached_buffer(cx));
721
722        // Let arrayBufferByteLength be O.[[ArrayBufferByteLength]].
723        // Step 3 (Reordered)
724        let buffer_length = self.byte_length();
725
726        // Let arrayBufferData be O.[[ArrayBufferData]].
727        // Step 2 (Reordered)
728        let buffer_data = match &self.buffer_source {
729            BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer) => unsafe {
730                StealArrayBufferContents(cx, Handle::from_raw(buffer.handle()))
731            },
732        };
733
734        // Perform ? DetachArrayBuffer(O).
735        // This will throw an exception if O has an [[ArrayBufferDetachKey]] that is not undefined,
736        // such as a WebAssembly.Memory’s buffer. [WASM-JS-API-1]
737        if !self.detach_buffer(cx) {
738            rooted!(&in(cx) let mut rval = UndefinedValue());
739            unsafe {
740                assert!(JS_GetPendingException(cx, rval.handle_mut()));
741                JS_ClearPendingException(cx)
742            };
743
744            Err(Error::Type(c"can't transfer array buffer".to_owned()))
745        } else {
746            // Return a new ArrayBuffer object, created in the current Realm,
747            // whose [[ArrayBufferData]] internal slot value is arrayBufferData and
748            // whose [[ArrayBufferByteLength]] internal slot value is arrayBufferByteLength.
749            rooted!(&in(cx) let result = unsafe {
750                NewArrayBufferWithContents(cx, buffer_length, buffer_data)
751            });
752            if result.is_null() {
753                return Err(Error::JSFailed);
754            }
755            Ok(RootedTraceableBox::new(
756                HeapBufferSource::<ArrayBufferU8>::new(result.handle()),
757            ))
758        }
759    }
760}
761
762unsafe impl<T> crate::dom::bindings::trace::JSTraceable for HeapBufferSource<T> {
763    #[inline]
764    unsafe fn trace(&self, tracer: *mut js::jsapi::JSTracer) {
765        match &self.buffer_source {
766            BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer) => {
767                unsafe { buffer.trace(tracer) };
768            },
769        }
770    }
771}
772
773/// <https://webidl.spec.whatwg.org/#arraybufferview-create>
774pub(crate) fn create_buffer_source<T>(
775    cx: &mut JSContext,
776    data: &[T::Element],
777    mut dest: MutableHandleObject,
778) -> Result<RootedTypedArray<T>, ()>
779where
780    T: TypedArrayElement + TypedArrayElementCreator,
781{
782    let res = unsafe {
783        TypedArray::<T, *mut JSObject>::create(cx, CreateWith::Slice(data), dest.reborrow())
784    };
785
786    if res.is_err() {
787        Err(())
788    } else {
789        TypedArray::from(dest.get()).map(RootedTraceableBox::new)
790    }
791}
792
793fn create_buffer_source_with_length<T>(
794    cx: &mut JSContext,
795    len: usize,
796    mut dest: MutableHandleObject,
797) -> Result<RootedTypedArray<T>, ()>
798where
799    T: TypedArrayElement + TypedArrayElementCreator,
800{
801    let res = unsafe {
802        TypedArray::<T, *mut JSObject>::create(cx, CreateWith::Length(len), dest.reborrow())
803    };
804
805    if res.is_err() {
806        Err(())
807    } else {
808        TypedArray::from(dest.get()).map(RootedTraceableBox::new)
809    }
810}
811
812pub(crate) fn byte_size(byte_type: Type) -> u64 {
813    match byte_type {
814        Type::Int8 | Type::Uint8 | Type::Uint8Clamped => 1,
815        Type::Int16 | Type::Uint16 | Type::Float16 => 2,
816        Type::Int32 | Type::Uint32 | Type::Float32 => 4,
817        Type::Int64 | Type::Float64 | Type::BigInt64 | Type::BigUint64 => 8,
818        Type::Simd128 => 16,
819        _ => unreachable!("invalid scalar type"),
820    }
821}
822
823#[derive(Clone, Eq, JSTraceable, MallocSizeOf, PartialEq)]
824pub(crate) enum Constructor {
825    DataView,
826    Name(
827        #[ignore_malloc_size_of = "mozjs"]
828        #[no_trace]
829        Type,
830    ),
831}
832
833pub(crate) fn create_buffer_source_with_constructor(
834    cx: &mut JSContext,
835    constructor: &Constructor,
836    buffer_source: &HeapBufferSource<ArrayBufferU8>,
837    byte_offset: usize,
838    byte_length: usize,
839) -> Fallible<RootedTraceableBox<HeapBufferSource<ArrayBufferViewU8>>> {
840    match &buffer_source.buffer_source {
841        BufferSource::ArrayBuffer(heap) => match constructor {
842            Constructor::DataView => {
843                rooted!(&in(cx) let view = unsafe {
844                    JS_NewDataView(
845                        cx,
846                        Handle::from_raw(heap.handle()),
847                        byte_offset,
848                        byte_length,
849                    )
850                });
851                if view.is_null() {
852                    return Err(Error::JSFailed);
853                }
854                Ok(RootedTraceableBox::new(HeapBufferSource::new(
855                    view.handle(),
856                )))
857            },
858            Constructor::Name(name_type) => construct_typed_array(
859                cx,
860                name_type,
861                buffer_source,
862                byte_offset,
863                byte_length as i64,
864            ),
865        },
866        BufferSource::ArrayBufferView(_) => {
867            unreachable!("Can not create a new ArrayBufferView from an existing ArrayBufferView");
868        },
869    }
870}
871
872/// Helper function to construct different TypedArray views
873fn construct_typed_array(
874    cx: &mut JSContext,
875    name_type: &Type,
876    buffer_source: &HeapBufferSource<ArrayBufferU8>,
877    byte_offset: usize,
878    byte_length: i64,
879) -> Fallible<RootedTraceableBox<HeapBufferSource<ArrayBufferViewU8>>> {
880    match &buffer_source.buffer_source {
881        BufferSource::ArrayBuffer(heap) => {
882            rooted!(&in(cx) let array_view = unsafe {
883                match name_type {
884                    Type::Int8 => JS_NewInt8ArrayWithBuffer(
885                        cx,
886                        Handle::from_raw(heap.handle()),
887                        byte_offset,
888                        byte_length,
889                    ),
890                    Type::Uint8 => JS_NewUint8ArrayWithBuffer(
891                        cx,
892                        Handle::from_raw(heap.handle()),
893                        byte_offset,
894                        byte_length,
895                    ),
896                    Type::Uint16 => JS_NewUint16ArrayWithBuffer(
897                        cx,
898                        Handle::from_raw(heap.handle()),
899                        byte_offset,
900                        byte_length,
901                    ),
902                    Type::Int16 => JS_NewInt16ArrayWithBuffer(
903                        cx,
904                        Handle::from_raw(heap.handle()),
905                        byte_offset,
906                        byte_length,
907                    ),
908                    Type::Int32 => JS_NewInt32ArrayWithBuffer(
909                        cx,
910                        Handle::from_raw(heap.handle()),
911                        byte_offset,
912                        byte_length,
913                    ),
914                    Type::Uint32 => JS_NewUint32ArrayWithBuffer(
915                        cx,
916                        Handle::from_raw(heap.handle()),
917                        byte_offset,
918                        byte_length,
919                    ),
920                    Type::Float32 => JS_NewFloat32ArrayWithBuffer(
921                        cx,
922                        Handle::from_raw(heap.handle()),
923                        byte_offset,
924                        byte_length,
925                    ),
926                    Type::Float64 => JS_NewFloat64ArrayWithBuffer(
927                        cx,
928                        Handle::from_raw(heap.handle()),
929                        byte_offset,
930                        byte_length,
931                    ),
932                    Type::Uint8Clamped => JS_NewUint8ClampedArrayWithBuffer(
933                        cx,
934                        Handle::from_raw(heap.handle()),
935                        byte_offset,
936                        byte_length,
937                    ),
938                    Type::BigInt64 => JS_NewBigInt64ArrayWithBuffer(
939                        cx,
940                        Handle::from_raw(heap.handle()),
941                        byte_offset,
942                        byte_length,
943                    ),
944                    Type::BigUint64 => JS_NewBigUint64ArrayWithBuffer(
945                        cx,
946                        Handle::from_raw(heap.handle()),
947                        byte_offset,
948                        byte_length,
949                    ),
950                    Type::Float16 => JS_NewFloat16ArrayWithBuffer(
951                        cx,
952                        Handle::from_raw(heap.handle()),
953                        byte_offset,
954                        byte_length,
955                    ),
956                    Type::Int64 | Type::Simd128 | Type::MaxTypedArrayViewType => {
957                        unreachable!("Invalid TypedArray type")
958                    },
959                }
960            });
961            if array_view.is_null() {
962                return Err(Error::JSFailed);
963            }
964
965            Ok(RootedTraceableBox::new(HeapBufferSource::new(
966                array_view.handle(),
967            )))
968        },
969        BufferSource::ArrayBufferView(_) => {
970            unreachable!("Can not create a new ArrayBufferView from an existing ArrayBufferView");
971        },
972    }
973}
974
975pub(crate) fn create_array_buffer_with_size(
976    cx: &mut JSContext,
977    size: usize,
978) -> Fallible<RootedTraceableBox<HeapBufferSource<ArrayBufferU8>>> {
979    rooted!(&in(cx) let result = unsafe { NewArrayBuffer(cx, size) });
980    if result.is_null() {
981        rooted!(&in(cx) let mut rval = UndefinedValue());
982        unsafe {
983            assert!(JS_GetPendingException(cx, rval.handle_mut()));
984            JS_ClearPendingException(cx)
985        };
986
987        Err(Error::Type(c"can't create array buffer".to_owned()))
988    } else {
989        Ok(RootedTraceableBox::new(
990            HeapBufferSource::<ArrayBufferU8>::new(result.handle()),
991        ))
992    }
993}