Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

webgl: Make a general way to get data from a JS array buffer view #8970

Merged
merged 4 commits into from Jan 5, 2016
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

@@ -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,15 +29,13 @@ 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;
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,14 +948,16 @@ 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);
}

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);
}
}

ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.