WebIDL: Use Uint8ClampedArray instead of raw JSObject in bindings (#31317)

* WebIDL: Use Uint8ClampedArray instead of raw JSObject in bindings

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>

* fmt

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>

* introduce new_initialized_heap_typed_array function

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>

* Remove unsed unsafe_code

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>

* Use doc comments for ImageData

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>

* Use get_internal instead of acquire_data

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>

* Handle JS errors in ImageData GetData and new_initialized_heap_typed_array

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>

* Fix wrong assert that causes CRASH in test

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>

* Early return for error

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>

* Address review comments

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>

---------

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>
This commit is contained in:
Taym Haddadi 2024-02-16 17:40:45 +01:00 committed by GitHub
parent 7e9be5ae9f
commit 328c376ff1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 118 additions and 55 deletions

View file

@ -130,6 +130,7 @@ builtinNames = {
IDLType.Tags.float64array: 'Float64Array', IDLType.Tags.float64array: 'Float64Array',
IDLType.Tags.arrayBuffer: 'ArrayBuffer', IDLType.Tags.arrayBuffer: 'ArrayBuffer',
IDLType.Tags.arrayBufferView: 'ArrayBufferView', IDLType.Tags.arrayBufferView: 'ArrayBufferView',
IDLType.Tags.uint8clampedarray: 'Uint8ClampedArray',
} }
numericTags = [ numericTags = [
@ -6520,6 +6521,7 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries
'js::typedarray::Float64Array', 'js::typedarray::Float64Array',
'js::typedarray::ArrayBuffer', 'js::typedarray::ArrayBuffer',
'js::typedarray::ArrayBufferView', 'js::typedarray::ArrayBufferView',
'js::typedarray::Uint8ClampedArray',
'crate::dom', 'crate::dom',
'crate::dom::bindings', 'crate::dom::bindings',
'crate::dom::bindings::codegen::InterfaceObjectMap', 'crate::dom::bindings::codegen::InterfaceObjectMap',

View file

@ -31,6 +31,38 @@ unsafe impl<T> crate::dom::bindings::trace::JSTraceable for HeapTypedArray<T> {
} }
} }
pub fn new_initialized_heap_typed_array<T>(
init: HeapTypedArrayInit,
) -> Result<HeapTypedArray<T>, ()>
where
T: TypedArrayElement + TypedArrayElementCreator,
T::Element: Clone + Copy,
{
let heap_typed_array = match init {
HeapTypedArrayInit::Object(js_object) => HeapTypedArray {
internal: Heap::boxed(js_object),
phantom: PhantomData::default(),
},
HeapTypedArrayInit::Info { len, cx } => {
rooted!(in (*cx) let mut array = ptr::null_mut::<JSObject>());
let typed_array_result =
create_typed_array_with_length::<T>(cx, len as usize, array.handle_mut());
if typed_array_result.is_err() {
return Err(());
}
let heap_typed_array = HeapTypedArray::<T>::default();
heap_typed_array.internal.set(*array);
heap_typed_array
},
};
Ok(heap_typed_array)
}
pub enum HeapTypedArrayInit {
Object(*mut JSObject),
Info { len: u32, cx: JSContext },
}
impl<T> HeapTypedArray<T> impl<T> HeapTypedArray<T>
where where
T: TypedArrayElement + TypedArrayElementCreator, T: TypedArrayElement + TypedArrayElementCreator,
@ -161,6 +193,23 @@ where
} }
} }
fn create_typed_array_with_length<T>(
cx: JSContext,
len: usize,
dest: MutableHandleObject,
) -> Result<TypedArray<T, *mut JSObject>, ()>
where
T: TypedArrayElement + TypedArrayElementCreator,
{
let res = unsafe { TypedArray::<T, *mut JSObject>::create(*cx, CreateWith::Length(len), dest) };
if res.is_err() {
Err(())
} else {
TypedArray::from(dest.get())
}
}
pub fn create_new_external_array_buffer<T>( pub fn create_new_external_array_buffer<T>(
cx: JSContext, cx: JSContext,
mapping: Arc<Mutex<Vec<T::Element>>>, mapping: Arc<Mutex<Vec<T::Element>>>,

View file

@ -3,18 +3,19 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::borrow::Cow; use std::borrow::Cow;
use std::default::Default;
use std::ptr; use std::ptr;
use std::ptr::NonNull;
use std::vec::Vec; use std::vec::Vec;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use euclid::default::{Rect, Size2D}; use euclid::default::{Rect, Size2D};
use ipc_channel::ipc::IpcSharedMemory; use ipc_channel::ipc::IpcSharedMemory;
use js::jsapi::{Heap, JSObject}; use js::jsapi::JSObject;
use js::rust::{HandleObject, Runtime}; use js::rust::HandleObject;
use js::typedarray::{CreateWith, Uint8ClampedArray}; use js::typedarray::{ClampedU8, CreateWith, Uint8ClampedArray};
use super::bindings::typedarrays::{
new_initialized_heap_typed_array, HeapTypedArray, HeapTypedArrayInit,
};
use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::ImageDataMethods; use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::ImageDataMethods;
use crate::dom::bindings::error::{Error, Fallible}; use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::reflector::{reflect_dom_object_with_proto, Reflector}; use crate::dom::bindings::reflector::{reflect_dom_object_with_proto, Reflector};
@ -28,7 +29,7 @@ pub struct ImageData {
width: u32, width: u32,
height: u32, height: u32,
#[ignore_malloc_size_of = "mozjs"] #[ignore_malloc_size_of = "mozjs"]
data: Heap<*mut JSObject>, data: HeapTypedArray<ClampedU8>,
} }
impl ImageData { impl ImageData {
@ -55,21 +56,30 @@ impl ImageData {
} }
#[allow(unsafe_code)] #[allow(unsafe_code)]
unsafe fn new_with_jsobject( fn new_with_jsobject(
global: &GlobalScope, global: &GlobalScope,
proto: Option<HandleObject>, proto: Option<HandleObject>,
width: u32, width: u32,
opt_height: Option<u32>, opt_height: Option<u32>,
jsobject: *mut JSObject, jsobject: *mut JSObject,
) -> Fallible<DomRoot<ImageData>> { ) -> Fallible<DomRoot<ImageData>> {
// checking jsobject type let heap_typed_array = match new_initialized_heap_typed_array::<ClampedU8>(
let cx = GlobalScope::get_cx(); HeapTypedArrayInit::Object(jsobject),
typedarray!(in(*cx) let array_res: Uint8ClampedArray = jsobject); ) {
let array = array_res.map_err(|_| { Ok(heap_typed_array) => heap_typed_array,
Error::Type("Argument to Image data is not an Uint8ClampedArray".to_owned()) Err(_) => return Err(Error::JSFailed),
})?; };
let byte_len = array.as_slice().len() as u32; let typed_array = match heap_typed_array.get_internal() {
Ok(array) => array,
Err(_) => {
return Err(Error::Type(
"Argument to Image data is not an Uint8ClampedArray".to_owned(),
))
},
};
let byte_len = unsafe { typed_array.as_slice().len() } as u32;
if byte_len == 0 || byte_len % 4 != 0 { if byte_len == 0 || byte_len % 4 != 0 {
return Err(Error::InvalidState); return Err(Error::InvalidState);
} }
@ -88,16 +98,13 @@ impl ImageData {
reflector_: Reflector::new(), reflector_: Reflector::new(),
width: width, width: width,
height: height, height: height,
data: Heap::default(), data: heap_typed_array,
}); });
(*imagedata).data.set(jsobject);
Ok(reflect_dom_object_with_proto(imagedata, global, proto)) Ok(reflect_dom_object_with_proto(imagedata, global, proto))
} }
#[allow(unsafe_code)] fn new_without_jsobject(
unsafe fn new_without_jsobject(
global: &GlobalScope, global: &GlobalScope,
proto: Option<HandleObject>, proto: Option<HandleObject>,
width: u32, width: u32,
@ -106,37 +113,40 @@ impl ImageData {
if width == 0 || height == 0 { if width == 0 || height == 0 {
return Err(Error::IndexSize); return Err(Error::IndexSize);
} }
let cx = GlobalScope::get_cx();
let len = width * height * 4;
let heap_typed_array =
match new_initialized_heap_typed_array::<ClampedU8>(HeapTypedArrayInit::Info {
len,
cx,
}) {
Ok(heap_typed_array) => heap_typed_array,
Err(_) => return Err(Error::JSFailed),
};
let imagedata = Box::new(ImageData { let imagedata = Box::new(ImageData {
reflector_: Reflector::new(), reflector_: Reflector::new(),
width: width, width: width,
height: height, height: height,
data: Heap::default(), data: heap_typed_array,
}); });
let len = width * height * 4;
let cx = GlobalScope::get_cx();
rooted!(in (*cx) let mut array = ptr::null_mut::<JSObject>());
Uint8ClampedArray::create(*cx, CreateWith::Length(len as usize), array.handle_mut())
.unwrap();
(*imagedata).data.set(array.get());
Ok(reflect_dom_object_with_proto(imagedata, global, proto)) Ok(reflect_dom_object_with_proto(imagedata, global, proto))
} }
// https://html.spec.whatwg.org/multipage/#pixel-manipulation:dom-imagedata-3 /// <https://html.spec.whatwg.org/multipage/#pixel-manipulation:dom-imagedata-3>
#[allow(unsafe_code, non_snake_case)] #[allow(non_snake_case)]
pub fn Constructor( pub fn Constructor(
global: &GlobalScope, global: &GlobalScope,
proto: Option<HandleObject>, proto: Option<HandleObject>,
width: u32, width: u32,
height: u32, height: u32,
) -> Fallible<DomRoot<Self>> { ) -> Fallible<DomRoot<Self>> {
unsafe { Self::new_without_jsobject(global, proto, width, height) } Self::new_without_jsobject(global, proto, width, height)
} }
// https://html.spec.whatwg.org/multipage/#pixel-manipulation:dom-imagedata-4 /// <https://html.spec.whatwg.org/multipage/#pixel-manipulation:dom-imagedata-4>
#[allow(unsafe_code, unused_variables, non_snake_case)] #[allow(unused_variables, non_snake_case)]
pub unsafe fn Constructor_( pub fn Constructor_(
cx: JSContext, cx: JSContext,
global: &GlobalScope, global: &GlobalScope,
proto: Option<HandleObject>, proto: Option<HandleObject>,
@ -150,17 +160,17 @@ impl ImageData {
/// Nothing must change the array on the JS side while the slice is live. /// Nothing must change the array on the JS side while the slice is live.
#[allow(unsafe_code)] #[allow(unsafe_code)]
pub unsafe fn as_slice(&self) -> &[u8] { pub unsafe fn as_slice(&self) -> &[u8] {
assert!(!self.data.get().is_null()); assert!(self.data.is_initialized());
let cx = Runtime::get(); let internal_data = self
assert!(!cx.is_null()); .data
typedarray!(in(cx) let array: Uint8ClampedArray = self.data.get()); .get_internal()
let array = array.as_ref().unwrap(); .expect("Failed to get Data from ImageData.");
// NOTE(nox): This is just as unsafe as `as_slice` itself even though we // NOTE(nox): This is just as unsafe as `as_slice` itself even though we
// are extending the lifetime of the slice, because the data in // are extending the lifetime of the slice, because the data in
// this ImageData instance will never change. The method is thus unsafe // this ImageData instance will never change. The method is thus unsafe
// because the array may be manipulated from JS while the reference // because the array may be manipulated from JS while the reference
// is live. // is live.
let ptr = array.as_slice() as *const _; let ptr: *const [u8] = internal_data.as_slice() as *const _;
&*ptr &*ptr
} }
@ -180,18 +190,18 @@ impl ImageData {
} }
impl ImageDataMethods for ImageData { impl ImageDataMethods for ImageData {
// https://html.spec.whatwg.org/multipage/#dom-imagedata-width /// <https://html.spec.whatwg.org/multipage/#dom-imagedata-width>
fn Width(&self) -> u32 { fn Width(&self) -> u32 {
self.width self.width
} }
// https://html.spec.whatwg.org/multipage/#dom-imagedata-height /// <https://html.spec.whatwg.org/multipage/#dom-imagedata-height>
fn Height(&self) -> u32 { fn Height(&self) -> u32 {
self.height self.height
} }
// https://html.spec.whatwg.org/multipage/#dom-imagedata-data /// <https://html.spec.whatwg.org/multipage/#dom-imagedata-data>
fn Data(&self, _: JSContext) -> NonNull<JSObject> { fn GetData(&self, _: JSContext) -> Fallible<Uint8ClampedArray> {
NonNull::new(self.data.get()).expect("got a null pointer") self.data.get_internal().map_err(|_| Error::JSFailed)
} }
} }

View file

@ -5,14 +5,14 @@
// check-tidy: no specs after this line // check-tidy: no specs after this line
use std::borrow::ToOwned; use std::borrow::ToOwned;
use std::ptr::NonNull; use std::ptr::{self, NonNull};
use std::rc::Rc; use std::rc::Rc;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use js::jsapi::{Heap, JSObject, JS_NewPlainObject, JS_NewUint8ClampedArray}; use js::jsapi::{Heap, JSObject, JS_NewPlainObject};
use js::jsval::{JSVal, NullValue}; use js::jsval::{JSVal, NullValue};
use js::rust::{CustomAutoRooterGuard, HandleObject, HandleValue}; use js::rust::{CustomAutoRooterGuard, HandleObject, HandleValue};
use js::typedarray; use js::typedarray::{self, Uint8ClampedArray};
use script_traits::serializable::BlobImpl; use script_traits::serializable::BlobImpl;
use script_traits::MsDuration; use script_traits::MsDuration;
use servo_config::prefs; use servo_config::prefs;
@ -41,6 +41,7 @@ use crate::dom::bindings::reflector::{reflect_dom_object_with_proto, DomObject,
use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::{ByteString, DOMString, USVString}; use crate::dom::bindings::str::{ByteString, DOMString, USVString};
use crate::dom::bindings::trace::RootedTraceableBox; use crate::dom::bindings::trace::RootedTraceableBox;
use crate::dom::bindings::typedarrays::create_typed_array;
use crate::dom::bindings::weakref::MutableWeakRef; use crate::dom::bindings::weakref::MutableWeakRef;
use crate::dom::blob::Blob; use crate::dom::blob::Blob;
use crate::dom::globalscope::GlobalScope; use crate::dom::globalscope::GlobalScope;
@ -209,12 +210,12 @@ impl TestBindingMethods for TestBinding {
ByteStringOrLong::ByteString(ByteString::new(vec![])) ByteStringOrLong::ByteString(ByteString::new(vec![]))
} }
fn SetUnion9Attribute(&self, _: ByteStringOrLong) {} fn SetUnion9Attribute(&self, _: ByteStringOrLong) {}
#[allow(unsafe_code)] fn ArrayAttribute(&self, cx: SafeJSContext) -> Uint8ClampedArray {
fn ArrayAttribute(&self, cx: SafeJSContext) -> NonNull<JSObject> { let data: [u8; 16] = [0; 16];
unsafe {
rooted!(in(*cx) let array = JS_NewUint8ClampedArray(*cx, 16)); rooted!(in (*cx) let mut array = ptr::null_mut::<JSObject>());
NonNull::new(array.get()).expect("got a null pointer") create_typed_array(cx, &data, array.handle_mut())
} .expect("Creating ClampedU8 array should never fail")
} }
fn AnyAttribute(&self, _: SafeJSContext) -> JSVal { fn AnyAttribute(&self, _: SafeJSContext) -> JSVal {
NullValue() NullValue()

View file

@ -259,6 +259,6 @@ interface ImageData {
readonly attribute unsigned long width; readonly attribute unsigned long width;
readonly attribute unsigned long height; readonly attribute unsigned long height;
readonly attribute Uint8ClampedArray data; [Throws] readonly attribute Uint8ClampedArray data;
//readonly attribute PredefinedColorSpace colorSpace; //readonly attribute PredefinedColorSpace colorSpace;
}; };

View file

@ -2411,6 +2411,7 @@ class IDLType(IDLObject):
"float64array", "float64array",
"arrayBuffer", "arrayBuffer",
"arrayBufferView", "arrayBufferView",
"uint8clampedarray",
"dictionary", "dictionary",
"enum", "enum",
"callback", "callback",
@ -3645,7 +3646,7 @@ class IDLBuiltinType(IDLType):
Types.ArrayBufferView: IDLType.Tags.arrayBufferView, Types.ArrayBufferView: IDLType.Tags.arrayBufferView,
Types.Int8Array: IDLType.Tags.int8array, Types.Int8Array: IDLType.Tags.int8array,
Types.Uint8Array: IDLType.Tags.uint8array, Types.Uint8Array: IDLType.Tags.uint8array,
Types.Uint8ClampedArray: IDLType.Tags.interface, Types.Uint8ClampedArray: IDLType.Tags.uint8clampedarray,
Types.Int16Array: IDLType.Tags.int16array, Types.Int16Array: IDLType.Tags.int16array,
Types.Uint16Array: IDLType.Tags.uint16array, Types.Uint16Array: IDLType.Tags.uint16array,
Types.Int32Array: IDLType.Tags.int32array, Types.Int32Array: IDLType.Tags.int32array,