mirror of
https://github.com/servo/servo.git
synced 2025-07-22 06:43:40 +01:00
Auto merge of #8970 - ecoal95:webgl-array-buffer-data, r=jdm
webgl: Make a general way to get data from a JS array buffer view This fixes an invalid length being reported from `float32_array_to_slice` (which used the byte length), and also to generalize getting data from a JS array buffer view, to reduce code duplication. The pending type safety issues, like where we could send a `UInt16Array` where we expect a `Float32` one, should be solved by IDL bindings in some cases, like `uniform[n]fv` or `vertexAttrib[n]fv`, and with extra checks in others, like in the pending `texImage2D(..., ArrayBufferView)`. r? @jdm <!-- Reviewable:start --> [<img src="https://reviewable.io/review_button.png" height=40 alt="Review on Reviewable"/>](https://reviewable.io/reviews/servo/servo/8970) <!-- Reviewable:end -->
This commit is contained in:
commit
4ab1cdece6
4 changed files with 146 additions and 65 deletions
|
@ -46,13 +46,15 @@ use js::glue::{RUST_JSID_IS_STRING, RUST_JSID_TO_STRING, UnwrapObject};
|
|||
use js::jsapi::{HandleId, HandleObject, HandleValue, JS_GetClass};
|
||||
use js::jsapi::{JSClass, JSContext, JSObject, MutableHandleValue};
|
||||
use js::jsapi::{JS_GetLatin1StringCharsAndLength, JS_GetReservedSlot};
|
||||
use js::jsapi::{JS_GetObjectAsArrayBufferView, JS_GetArrayBufferViewType};
|
||||
use js::jsapi::{JS_GetTwoByteStringCharsAndLength, JS_NewStringCopyN};
|
||||
use js::jsapi::{JS_StringHasLatin1Chars, JS_WrapValue};
|
||||
use js::jsapi::{Type};
|
||||
use js::jsval::{ObjectValue, StringValue};
|
||||
use js::rust::ToString;
|
||||
use libc;
|
||||
use num::Float;
|
||||
use std::{ptr, slice};
|
||||
use std::{ptr, mem, slice};
|
||||
use util::str::DOMString;
|
||||
pub use util::str::{StringificationBehavior, jsstring_to_str};
|
||||
|
||||
|
@ -335,3 +337,101 @@ impl<T: Reflectable> ToJSValConvertible for Root<T> {
|
|||
self.reflector().to_jsval(cx, rval);
|
||||
}
|
||||
}
|
||||
|
||||
/// A JS ArrayBufferView contents can only be viewed as the types marked with this trait
|
||||
pub unsafe trait ArrayBufferViewContents: Clone {
|
||||
/// Check if the JS ArrayBufferView type is compatible with the implementor of the
|
||||
/// trait
|
||||
fn is_type_compatible(ty: Type) -> bool;
|
||||
}
|
||||
|
||||
unsafe impl ArrayBufferViewContents for u8 {
|
||||
fn is_type_compatible(ty: Type) -> bool {
|
||||
match ty {
|
||||
Type::Uint8 |
|
||||
Type::Uint8Clamped => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl ArrayBufferViewContents for i8 {
|
||||
fn is_type_compatible(ty: Type) -> bool {
|
||||
ty as i32 == Type::Int8 as i32
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl ArrayBufferViewContents for u16 {
|
||||
fn is_type_compatible(ty: Type) -> bool {
|
||||
ty as i32 == Type::Uint16 as i32
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl ArrayBufferViewContents for i16 {
|
||||
fn is_type_compatible(ty: Type) -> bool {
|
||||
ty as i32 == Type::Int16 as i32
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl ArrayBufferViewContents for u32 {
|
||||
fn is_type_compatible(ty: Type) -> bool {
|
||||
ty as i32 == Type::Uint32 as i32
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl ArrayBufferViewContents for i32 {
|
||||
fn is_type_compatible(ty: Type) -> bool {
|
||||
ty as i32 == Type::Int32 as i32
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl ArrayBufferViewContents for f32 {
|
||||
fn is_type_compatible(ty: Type) -> bool {
|
||||
ty as i32 == Type::Float32 as i32
|
||||
}
|
||||
}
|
||||
unsafe impl ArrayBufferViewContents for f64 {
|
||||
fn is_type_compatible(ty: Type) -> bool {
|
||||
ty as i32 == Type::Float64 as i32
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a mutable slice of the Array Buffer View data, viewed as T, without checking the real
|
||||
/// type of it.
|
||||
pub unsafe fn array_buffer_view_data<'a, T: ArrayBufferViewContents>(abv: *mut JSObject) -> Option<&'a mut [T]> {
|
||||
let mut byte_length = 0;
|
||||
let mut ptr = ptr::null_mut();
|
||||
let ret = JS_GetObjectAsArrayBufferView(abv, &mut byte_length, &mut ptr);
|
||||
if ret.is_null() {
|
||||
return None;
|
||||
}
|
||||
Some(slice::from_raw_parts_mut(ptr as *mut T, byte_length as usize / mem::size_of::<T>()))
|
||||
}
|
||||
|
||||
/// Returns a copy of the ArrayBufferView data, viewed as T, without checking the real type of it.
|
||||
pub fn array_buffer_view_to_vec<T: ArrayBufferViewContents>(abv: *mut JSObject) -> Option<Vec<T>> {
|
||||
unsafe {
|
||||
array_buffer_view_data(abv).map(|data| data.to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a mutable slice of the Array Buffer View data, viewed as T, checking that the real type
|
||||
/// of it is ty.
|
||||
pub unsafe fn array_buffer_view_data_checked<'a, T: ArrayBufferViewContents>(abv: *mut JSObject)
|
||||
-> Option<&'a mut [T]> {
|
||||
array_buffer_view_data::<T>(abv).and_then(|data| {
|
||||
if T::is_type_compatible(JS_GetArrayBufferViewType(abv)) {
|
||||
Some(data)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a copy of the ArrayBufferView data, viewed as T, checking that the real type
|
||||
/// of it is ty.
|
||||
pub fn array_buffer_view_to_vec_checked<T: ArrayBufferViewContents>(abv: *mut JSObject) -> Option<Vec<T>> {
|
||||
unsafe {
|
||||
array_buffer_view_data_checked(abv).map(|data| data.to_vec())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,14 +5,14 @@
|
|||
use dom::bindings::cell::DOMRefCell;
|
||||
use dom::bindings::codegen::Bindings::CryptoBinding;
|
||||
use dom::bindings::codegen::Bindings::CryptoBinding::CryptoMethods;
|
||||
use dom::bindings::conversions::array_buffer_view_data;
|
||||
use dom::bindings::error::{Error, Fallible};
|
||||
use dom::bindings::global::GlobalRef;
|
||||
use dom::bindings::js::Root;
|
||||
use dom::bindings::reflector::{Reflector, reflect_dom_object};
|
||||
use js::jsapi::{JSContext, JSObject};
|
||||
use js::jsapi::{JS_GetArrayBufferViewType, JS_GetObjectAsArrayBufferView, Type};
|
||||
use js::jsapi::{JS_GetArrayBufferViewType, Type};
|
||||
use rand::{OsRng, Rng};
|
||||
use std::{ptr, slice};
|
||||
|
||||
no_jsmanaged_fields!(OsRng);
|
||||
|
||||
|
@ -43,24 +43,23 @@ impl CryptoMethods for Crypto {
|
|||
_cx: *mut JSContext,
|
||||
input: *mut JSObject)
|
||||
-> Fallible<*mut JSObject> {
|
||||
let mut length = 0;
|
||||
let mut data = ptr::null_mut();
|
||||
if unsafe { JS_GetObjectAsArrayBufferView(input, &mut length, &mut data).is_null() } {
|
||||
return Err(Error::Type("Argument to Crypto.getRandomValues is not an ArrayBufferView"
|
||||
let mut data = match unsafe { array_buffer_view_data::<u8>(input) } {
|
||||
Some(data) => data,
|
||||
None => {
|
||||
return Err(Error::Type("Argument to Crypto.getRandomValues is not an ArrayBufferView"
|
||||
.to_owned()));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if !is_integer_buffer(input) {
|
||||
return Err(Error::TypeMismatch);
|
||||
}
|
||||
if length > 65536 {
|
||||
|
||||
if data.len() > 65536 {
|
||||
return Err(Error::QuotaExceeded);
|
||||
}
|
||||
|
||||
let mut buffer = unsafe {
|
||||
slice::from_raw_parts_mut(data, length as usize)
|
||||
};
|
||||
|
||||
self.rng.borrow_mut().fill_bytes(&mut buffer);
|
||||
self.rng.borrow_mut().fill_bytes(&mut data);
|
||||
|
||||
Ok(input)
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
use dom::bindings::codegen::Bindings::TextDecoderBinding;
|
||||
use dom::bindings::codegen::Bindings::TextDecoderBinding::TextDecoderMethods;
|
||||
use dom::bindings::conversions::array_buffer_view_data;
|
||||
use dom::bindings::error::{Error, Fallible};
|
||||
use dom::bindings::global::GlobalRef;
|
||||
use dom::bindings::js::Root;
|
||||
|
@ -13,10 +14,8 @@ use dom::bindings::trace::JSTraceable;
|
|||
use encoding::Encoding;
|
||||
use encoding::label::encoding_from_whatwg_label;
|
||||
use encoding::types::{DecoderTrap, EncodingRef};
|
||||
use js::jsapi::JS_GetObjectAsArrayBufferView;
|
||||
use js::jsapi::{JSContext, JSObject};
|
||||
use std::borrow::ToOwned;
|
||||
use std::{ptr, slice};
|
||||
use util::str::DOMString;
|
||||
|
||||
#[dom_struct]
|
||||
|
@ -89,21 +88,20 @@ impl TextDecoderMethods for TextDecoder {
|
|||
None => return Ok(USVString("".to_owned())),
|
||||
};
|
||||
|
||||
let mut length = 0;
|
||||
let mut data = ptr::null_mut();
|
||||
if unsafe { JS_GetObjectAsArrayBufferView(input, &mut length, &mut data).is_null() } {
|
||||
return Err(Error::Type("Argument to TextDecoder.decode is not an ArrayBufferView".to_owned()));
|
||||
}
|
||||
|
||||
let buffer = unsafe {
|
||||
slice::from_raw_parts(data as *const _, length as usize)
|
||||
let data = match unsafe { array_buffer_view_data::<u8>(input) } {
|
||||
Some(data) => data,
|
||||
None => {
|
||||
return Err(Error::Type("Argument to TextDecoder.decode is not an ArrayBufferView".to_owned()));
|
||||
}
|
||||
};
|
||||
|
||||
let trap = if self.fatal {
|
||||
DecoderTrap::Strict
|
||||
} else {
|
||||
DecoderTrap::Replace
|
||||
};
|
||||
match self.encoding.decode(buffer, trap) {
|
||||
|
||||
match self.encoding.decode(data, trap) {
|
||||
Ok(s) => Ok(USVString(s)),
|
||||
Err(_) => Err(Error::Type("Decoding failed".to_owned())),
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderi
|
|||
use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::{WebGLRenderingContextMethods};
|
||||
use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::{self, WebGLContextAttributes};
|
||||
use dom::bindings::codegen::UnionTypes::ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement;
|
||||
use dom::bindings::conversions::ToJSValConvertible;
|
||||
use dom::bindings::conversions::{ToJSValConvertible, array_buffer_view_to_vec_checked, array_buffer_view_to_vec};
|
||||
use dom::bindings::global::{GlobalField, GlobalRef};
|
||||
use dom::bindings::inheritance::Castable;
|
||||
use dom::bindings::js::{JS, LayoutJS, MutNullableHeap, Root};
|
||||
|
@ -29,7 +29,6 @@ use dom::webgluniformlocation::WebGLUniformLocation;
|
|||
use euclid::size::Size2D;
|
||||
use ipc_channel::ipc::{self, IpcSender};
|
||||
use js::jsapi::{JSContext, JSObject, RootedValue};
|
||||
use js::jsapi::{JS_GetFloat32ArrayData, JS_GetObjectAsArrayBufferView};
|
||||
use js::jsval::{BooleanValue, DoubleValue, Int32Value, JSVal, NullValue, UndefinedValue};
|
||||
use net_traits::image::base::PixelFormat;
|
||||
use net_traits::image_cache_task::ImageResponse;
|
||||
|
@ -37,7 +36,6 @@ use offscreen_gl_context::GLContextAttributes;
|
|||
use script_traits::ScriptMsg as ConstellationMsg;
|
||||
use std::cell::Cell;
|
||||
use std::sync::mpsc::channel;
|
||||
use std::{ptr, slice};
|
||||
use util::str::DOMString;
|
||||
use util::vec::byte_swap;
|
||||
|
||||
|
@ -166,35 +164,6 @@ impl WebGLRenderingContext {
|
|||
self.canvas.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
fn float32_array_to_slice(&self, data: *mut JSObject) -> Option<Vec<f32>> {
|
||||
unsafe {
|
||||
let mut length = 0;
|
||||
let mut ptr = ptr::null_mut();
|
||||
let buffer_data = JS_GetObjectAsArrayBufferView(data, &mut length, &mut ptr);
|
||||
if buffer_data.is_null() {
|
||||
self.webgl_error(InvalidValue); // https://github.com/servo/servo/issues/5014
|
||||
return None;
|
||||
}
|
||||
let data_f32 = JS_GetFloat32ArrayData(data, ptr::null());
|
||||
Some(slice::from_raw_parts(data_f32, length as usize).to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
fn byte_array_to_slice(&self, data: *mut JSObject) -> Option<Vec<u8>> {
|
||||
unsafe {
|
||||
let mut length = 0;
|
||||
let mut ptr = ptr::null_mut();
|
||||
let buffer_data = JS_GetObjectAsArrayBufferView(data, &mut length, &mut ptr);
|
||||
if buffer_data.is_null() {
|
||||
self.webgl_error(InvalidValue); // https://github.com/servo/servo/issues/5014
|
||||
return None;
|
||||
}
|
||||
Some(slice::from_raw_parts(ptr, length as usize).to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
fn vertex_attrib(&self, indx: u32, x: f32, y: f32, z: f32, w: f32) {
|
||||
self.ipc_renderer
|
||||
.send(CanvasMsg::WebGL(CanvasWebGLMsg::VertexAttrib(indx, x, y, z, w)))
|
||||
|
@ -471,8 +440,12 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
|
|||
Some(data) => data,
|
||||
None => return self.webgl_error(InvalidValue),
|
||||
};
|
||||
if let Some(data_vec) = self.byte_array_to_slice(data) {
|
||||
if let Some(data_vec) = array_buffer_view_to_vec::<u8>(data) {
|
||||
handle_potential_webgl_error!(self, bound_buffer.buffer_data(target, &data_vec, usage));
|
||||
} else {
|
||||
// NB: array_buffer_view_to_vec should never fail when
|
||||
// we have WebIDL support for Float32Array etc.
|
||||
self.webgl_error(InvalidValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -495,13 +468,15 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
|
|||
if offset < 0 {
|
||||
return self.webgl_error(InvalidValue);
|
||||
}
|
||||
if let Some(data_vec) = self.byte_array_to_slice(data) {
|
||||
if let Some(data_vec) = array_buffer_view_to_vec::<u8>(data) {
|
||||
if (offset as usize) + data_vec.len() > bound_buffer.capacity() {
|
||||
return self.webgl_error(InvalidValue);
|
||||
}
|
||||
self.ipc_renderer
|
||||
.send(CanvasMsg::WebGL(CanvasWebGLMsg::BufferSubData(target, offset as isize, data_vec)))
|
||||
.unwrap()
|
||||
} else {
|
||||
self.webgl_error(InvalidValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -973,7 +948,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
|
|||
None => return,
|
||||
};
|
||||
|
||||
if let Some(data_vec) = self.float32_array_to_slice(data) {
|
||||
if let Some(data_vec) = array_buffer_view_to_vec_checked::<f32>(data) {
|
||||
if data_vec.len() < 4 {
|
||||
return self.webgl_error(InvalidOperation);
|
||||
}
|
||||
|
@ -981,6 +956,8 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
|
|||
self.ipc_renderer
|
||||
.send(CanvasMsg::WebGL(CanvasWebGLMsg::Uniform4fv(uniform_id, data_vec)))
|
||||
.unwrap()
|
||||
} else {
|
||||
self.webgl_error(InvalidValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -999,11 +976,13 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
|
|||
#[allow(unsafe_code)]
|
||||
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
|
||||
fn VertexAttrib1fv(&self, _cx: *mut JSContext, indx: u32, data: *mut JSObject) {
|
||||
if let Some(data_vec) = self.float32_array_to_slice(data) {
|
||||
if let Some(data_vec) = array_buffer_view_to_vec_checked::<f32>(data) {
|
||||
if data_vec.len() < 4 {
|
||||
return self.webgl_error(InvalidOperation);
|
||||
}
|
||||
self.vertex_attrib(indx, data_vec[0], 0f32, 0f32, 1f32)
|
||||
} else {
|
||||
self.webgl_error(InvalidValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1015,11 +994,13 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
|
|||
#[allow(unsafe_code)]
|
||||
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
|
||||
fn VertexAttrib2fv(&self, _cx: *mut JSContext, indx: u32, data: *mut JSObject) {
|
||||
if let Some(data_vec) = self.float32_array_to_slice(data) {
|
||||
if let Some(data_vec) = array_buffer_view_to_vec_checked::<f32>(data) {
|
||||
if data_vec.len() < 2 {
|
||||
return self.webgl_error(InvalidOperation);
|
||||
}
|
||||
self.vertex_attrib(indx, data_vec[0], data_vec[1], 0f32, 1f32)
|
||||
} else {
|
||||
self.webgl_error(InvalidValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1031,11 +1012,13 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
|
|||
#[allow(unsafe_code)]
|
||||
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
|
||||
fn VertexAttrib3fv(&self, _cx: *mut JSContext, indx: u32, data: *mut JSObject) {
|
||||
if let Some(data_vec) = self.float32_array_to_slice(data) {
|
||||
if let Some(data_vec) = array_buffer_view_to_vec_checked::<f32>(data) {
|
||||
if data_vec.len() < 3 {
|
||||
return self.webgl_error(InvalidOperation);
|
||||
}
|
||||
self.vertex_attrib(indx, data_vec[0], data_vec[1], data_vec[2], 1f32)
|
||||
} else {
|
||||
self.webgl_error(InvalidValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1044,14 +1027,15 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
|
|||
self.vertex_attrib(indx, x, y, z, w)
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
|
||||
fn VertexAttrib4fv(&self, _cx: *mut JSContext, indx: u32, data: *mut JSObject) {
|
||||
if let Some(data_vec) = self.float32_array_to_slice(data) {
|
||||
if let Some(data_vec) = array_buffer_view_to_vec_checked::<f32>(data) {
|
||||
if data_vec.len() < 4 {
|
||||
return self.webgl_error(InvalidOperation);
|
||||
}
|
||||
self.vertex_attrib(indx, data_vec[0], data_vec[1], data_vec[2], data_vec[3])
|
||||
} else {
|
||||
self.webgl_error(InvalidValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue