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 1 commit
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

conversion: Extrapolate array_buffer_view_data

And use it instead of the raw jsapi calls.
  • Loading branch information
emilio committed Jan 4, 2016
commit 9ad49c8aa1524f597707aaa4ea937a48534b2415
@@ -48,11 +48,12 @@ use js::jsapi::{JSClass, JSContext, JSObject, MutableHandleValue};
use js::jsapi::{JS_GetLatin1StringCharsAndLength, JS_GetReservedSlot};
use js::jsapi::{JS_GetTwoByteStringCharsAndLength, JS_NewStringCopyN};
use js::jsapi::{JS_StringHasLatin1Chars, JS_WrapValue};
use js::jsapi::{JS_GetObjectAsArrayBufferView};
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 +336,32 @@ 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 {}
unsafe impl ArrayBufferViewContents for u8 {}
unsafe impl ArrayBufferViewContents for i8 {}
unsafe impl ArrayBufferViewContents for u16 {}
unsafe impl ArrayBufferViewContents for i16 {}
unsafe impl ArrayBufferViewContents for u32 {}
unsafe impl ArrayBufferViewContents for i32 {}
unsafe impl ArrayBufferViewContents for f32 {}
unsafe impl ArrayBufferViewContents for f64 {}

/// Returns a mutable slice of the Array Buffer View data, viewed as T
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
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())
}
}
@@ -3,16 +3,16 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::cell::DOMRefCell;
use dom::bindings::conversions::array_buffer_view_data;
use dom::bindings::codegen::Bindings::CryptoBinding;
use dom::bindings::codegen::Bindings::CryptoBinding::CryptoMethods;
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};
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_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, mem};
use util::str::DOMString;
use util::vec::byte_swap;

@@ -166,20 +164,6 @@ impl WebGLRenderingContext {
self.canvas.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
}

#[allow(unsafe_code)]
fn array_buffer_view_to_vec<T: Clone>(&self, data: *mut JSObject) -> Option<Vec<T>> {
unsafe {
let mut byte_length = 0;
let mut ptr = ptr::null_mut();
let buffer_data = JS_GetObjectAsArrayBufferView(data, &mut byte_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 as *mut T, byte_length as usize / mem::size_of::<T>()).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)))
@@ -456,8 +440,12 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
Some(data) => data,
None => return self.webgl_error(InvalidValue),
};
if let Some(data_vec) = self.array_buffer_view_to_vec::<u8>(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);
}
}

@@ -480,13 +468,15 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
if offset < 0 {
return self.webgl_error(InvalidValue);
}
if let Some(data_vec) = self.array_buffer_view_to_vec::<u8>(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);
}
}

@@ -958,14 +948,16 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
None => return,
};

if let Some(data_vec) = self.array_buffer_view_to_vec::<f32>(data) {
if let Some(data_vec) = array_buffer_view_to_vec::<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);
}
}

@@ -984,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.array_buffer_view_to_vec::<f32>(data) {
if let Some(data_vec) = array_buffer_view_to_vec::<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);
}
}

@@ -1000,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.array_buffer_view_to_vec::<f32>(data) {
if let Some(data_vec) = array_buffer_view_to_vec::<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);
}
}

@@ -1016,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.array_buffer_view_to_vec::<f32>(data) {
if let Some(data_vec) = array_buffer_view_to_vec::<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);
}
}

@@ -1032,11 +1030,13 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
#[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.array_buffer_view_to_vec::<f32>(data) {
if let Some(data_vec) = array_buffer_view_to_vec::<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.