Auto merge of #21723 - servo:webgl, r=jdm

More drive-by WebGL and canvas fixes

<!-- Reviewable:start -->
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/21723)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2018-09-18 11:06:51 -04:00 committed by GitHub
commit 82e94e3b67
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 301 additions and 301 deletions

View file

@ -27,7 +27,7 @@ use dom::canvasgradient::{CanvasGradient, CanvasGradientStyle, ToFillOrStrokeSty
use dom::canvaspattern::CanvasPattern;
use dom::element::Element;
use dom::globalscope::GlobalScope;
use dom::htmlcanvaselement::HTMLCanvasElement;
use dom::htmlcanvaselement::{CanvasContext, HTMLCanvasElement};
use dom::imagedata::ImageData;
use dom::node::{Node, NodeDamage, window_from_node};
use dom_struct::dom_struct;
@ -247,8 +247,7 @@ impl CanvasRenderingContext2D {
canvas.origin_is_clean()
}
CanvasImageSource::HTMLImageElement(image) => {
let image_origin = image.get_origin().expect("Image's origin is missing");
image_origin.same_origin(GlobalScope::entry().origin())
image.same_origin(GlobalScope::entry().origin())
}
CanvasImageSource::CSSStyleValue(_) => true,
}
@ -315,17 +314,18 @@ impl CanvasRenderingContext2D {
result
}
fn draw_html_canvas_element(&self,
canvas: &HTMLCanvasElement,
sx: f64,
sy: f64,
sw: Option<f64>,
sh: Option<f64>,
dx: f64,
dy: f64,
dw: Option<f64>,
dh: Option<f64>)
-> ErrorResult {
fn draw_html_canvas_element(
&self,
canvas: &HTMLCanvasElement,
sx: f64,
sy: f64,
sw: Option<f64>,
sh: Option<f64>,
dx: f64,
dy: f64,
dw: Option<f64>,
dh: Option<f64>,
) -> ErrorResult {
// 1. Check the usability of the image argument
if !canvas.is_valid() {
return Err(Error::InvalidState);
@ -339,15 +339,17 @@ impl CanvasRenderingContext2D {
let image_size = Size2D::new(canvas_size.width as f64, canvas_size.height as f64);
// 2. Establish the source and destination rectangles
let (source_rect, dest_rect) = self.adjust_source_dest_rects(image_size,
sx,
sy,
sw,
sh,
dx,
dy,
dw,
dh);
let (source_rect, dest_rect) = self.adjust_source_dest_rects(
image_size,
sx,
sy,
sw,
sh,
dx,
dy,
dw,
dh,
);
if !is_rect_valid(source_rect) || !is_rect_valid(dest_rect) {
return Ok(());
@ -355,29 +357,28 @@ impl CanvasRenderingContext2D {
let smoothing_enabled = self.state.borrow().image_smoothing_enabled;
if self.canvas.as_ref().map_or(false, |c| &**c == canvas) {
self.send_canvas_2d_msg(Canvas2dMsg::DrawImageSelf(
image_size, dest_rect, source_rect, smoothing_enabled));
if let Some(context) = canvas.context() {
match *context {
CanvasContext::Context2d(ref context) => {
context.send_canvas_2d_msg(Canvas2dMsg::DrawImageInOther(
self.get_canvas_id(),
image_size,
dest_rect,
source_rect,
smoothing_enabled,
));
},
_ => return Err(Error::InvalidState),
}
} else {
let context = match canvas.get_or_init_2d_context() {
Some(context) => context,
None => return Err(Error::InvalidState),
};
let msg = CanvasMsg::Canvas2d(
Canvas2dMsg::DrawImageInOther(
self.get_canvas_id(),
image_size,
dest_rect,
source_rect,
smoothing_enabled
),
context.get_canvas_id()
);
let renderer = context.get_ipc_renderer();
renderer.send(msg).unwrap();
};
self.send_canvas_2d_msg(Canvas2dMsg::DrawImage(
None,
image_size,
dest_rect,
source_rect,
smoothing_enabled,
));
}
self.mark_as_dirty();
Ok(())
@ -447,7 +448,7 @@ impl CanvasRenderingContext2D {
let smoothing_enabled = self.state.borrow().image_smoothing_enabled;
self.send_canvas_2d_msg(Canvas2dMsg::DrawImage(
image_data.into(),
Some(image_data.into()),
image_size,
dest_rect,
source_rect,
@ -1205,8 +1206,6 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D {
.ok_or(Error::InvalidState)?
},
CanvasImageSource::HTMLCanvasElement(ref canvas) => {
let _ = canvas.get_or_init_2d_context();
canvas.fetch_all_data().ok_or(Error::InvalidState)?
},
CanvasImageSource::CSSStyleValue(ref value) => {

View file

@ -37,9 +37,10 @@ use js::jsapi::JSContext;
use js::rust::HandleValue;
use offscreen_gl_context::GLContextAttributes;
use profile_traits::ipc;
use ref_filter_map;
use script_layout_interface::{HTMLCanvasData, HTMLCanvasDataSource};
use servo_config::prefs::PREFS;
use std::iter::repeat;
use std::cell::Ref;
use style::attr::{AttrValue, LengthOrPercentageOrAuto};
const DEFAULT_WIDTH: u32 = 300;
@ -174,42 +175,46 @@ impl LayoutHTMLCanvasElementHelpers for LayoutDom<HTMLCanvasElement> {
impl HTMLCanvasElement {
pub fn get_or_init_2d_context(&self) -> Option<DomRoot<CanvasRenderingContext2D>> {
if self.context.borrow().is_none() {
let window = window_from_node(self);
let size = self.get_size();
let context = CanvasRenderingContext2D::new(window.upcast::<GlobalScope>(), self, size);
*self.context.borrow_mut() = Some(CanvasContext::Context2d(Dom::from_ref(&*context)));
}
match *self.context.borrow().as_ref().unwrap() {
CanvasContext::Context2d(ref context) => Some(DomRoot::from_ref(&*context)),
_ => None,
}
pub fn context(&self) -> Option<Ref<CanvasContext>> {
ref_filter_map::ref_filter_map(self.context.borrow(), |ctx| ctx.as_ref())
}
pub fn get_or_init_webgl_context(
fn get_or_init_2d_context(&self) -> Option<DomRoot<CanvasRenderingContext2D>> {
if let Some(ctx) = self.context() {
return match *ctx {
CanvasContext::Context2d(ref ctx) => Some(DomRoot::from_ref(ctx)),
_ => None,
};
}
let window = window_from_node(self);
let size = self.get_size();
let context = CanvasRenderingContext2D::new(window.upcast::<GlobalScope>(), self, size);
*self.context.borrow_mut() = Some(CanvasContext::Context2d(Dom::from_ref(&*context)));
Some(context)
}
#[allow(unsafe_code)]
unsafe fn get_or_init_webgl_context(
&self,
cx: *mut JSContext,
options: HandleValue,
) -> Option<DomRoot<WebGLRenderingContext>> {
if self.context.borrow().is_none() {
let window = window_from_node(self);
let size = self.get_size();
let attrs = Self::get_gl_attributes(cx, options)?;
let maybe_ctx = WebGLRenderingContext::new(&window, self, WebGLVersion::WebGL1, size, attrs);
*self.context.borrow_mut() = maybe_ctx.map( |ctx| CanvasContext::WebGL(Dom::from_ref(&*ctx)));
}
if let Some(CanvasContext::WebGL(ref context)) = *self.context.borrow() {
Some(DomRoot::from_ref(&*context))
} else {
None
if let Some(ctx) = self.context() {
return match *ctx {
CanvasContext::WebGL(ref ctx) => Some(DomRoot::from_ref(ctx)),
_ => None,
};
}
let window = window_from_node(self);
let size = self.get_size();
let attrs = Self::get_gl_attributes(cx, options)?;
let context = WebGLRenderingContext::new(&window, self, WebGLVersion::WebGL1, size, attrs)?;
*self.context.borrow_mut() = Some(CanvasContext::WebGL(Dom::from_ref(&*context)));
Some(context)
}
pub fn get_or_init_webgl2_context(
#[allow(unsafe_code)]
unsafe fn get_or_init_webgl2_context(
&self,
cx: *mut JSContext,
options: HandleValue,
@ -217,20 +222,18 @@ impl HTMLCanvasElement {
if !PREFS.is_webgl2_enabled() {
return None
}
if self.context.borrow().is_none() {
let window = window_from_node(self);
let size = self.get_size();
let attrs = Self::get_gl_attributes(cx, options)?;
let maybe_ctx = WebGL2RenderingContext::new(&window, self, size, attrs);
*self.context.borrow_mut() = maybe_ctx.map( |ctx| CanvasContext::WebGL2(Dom::from_ref(&*ctx)));
}
if let Some(CanvasContext::WebGL2(ref context)) = *self.context.borrow() {
Some(DomRoot::from_ref(&*context))
} else {
None
if let Some(ctx) = self.context() {
return match *ctx {
CanvasContext::WebGL2(ref ctx) => Some(DomRoot::from_ref(ctx)),
_ => None,
};
}
let window = window_from_node(self);
let size = self.get_size();
let attrs = Self::get_gl_attributes(cx, options)?;
let context = WebGL2RenderingContext::new(&window, self, size, attrs)?;
*self.context.borrow_mut() = Some(CanvasContext::WebGL2(Dom::from_ref(&*context)));
Some(context)
}
/// Gets the base WebGLRenderingContext for WebGL or WebGL 2, if exists.
@ -243,11 +246,14 @@ impl HTMLCanvasElement {
}
#[allow(unsafe_code)]
fn get_gl_attributes(cx: *mut JSContext, options: HandleValue) -> Option<GLContextAttributes> {
match unsafe { WebGLContextAttributes::new(cx, options) } {
unsafe fn get_gl_attributes(
cx: *mut JSContext,
options: HandleValue,
) -> Option<GLContextAttributes> {
match WebGLContextAttributes::new(cx, options) {
Ok(ConversionResult::Success(ref attrs)) => Some(From::from(attrs)),
Ok(ConversionResult::Failure(ref error)) => {
unsafe { throw_type_error(cx, &error); }
throw_type_error(cx, &error);
None
}
_ => {
@ -284,9 +290,7 @@ impl HTMLCanvasElement {
// TODO: add a method in WebGL2RenderingContext to get the pixels.
return None;
},
None => {
repeat(0xffu8).take((size.height as usize) * (size.width as usize) * 4).collect()
}
None => vec![0; size.height as usize * size.width as usize * 4]
};
Some((data, size))

View file

@ -55,7 +55,7 @@ use network_listener::{NetworkListener, PreInvoke};
use num_traits::ToPrimitive;
use script_thread::ScriptThread;
use servo_url::ServoUrl;
use servo_url::origin::ImmutableOrigin;
use servo_url::origin::MutableOrigin;
use std::cell::{Cell, RefMut};
use std::char;
use std::collections::HashSet;
@ -1186,11 +1186,10 @@ impl HTMLImageElement {
useMapElements.map(|mapElem| mapElem.get_area_elements())
}
pub fn get_origin(&self) -> Option<ImmutableOrigin> {
match self.current_request.borrow_mut().final_url {
Some(ref url) => Some(url.origin()),
None => None
}
pub fn same_origin(&self, origin: &MutableOrigin) -> bool {
self.current_request.borrow_mut().final_url.as_ref().map_or(false, |url| {
url.scheme() == "data" || url.origin().same_origin(origin)
})
}
}

View file

@ -13,15 +13,16 @@ use canvas_traits::webgl::WebGLError::*;
use dom::bindings::codegen::Bindings::ANGLEInstancedArraysBinding::ANGLEInstancedArraysConstants;
use dom::bindings::codegen::Bindings::EXTBlendMinmaxBinding::EXTBlendMinmaxConstants;
use dom::bindings::codegen::Bindings::OESVertexArrayObjectBinding::OESVertexArrayObjectConstants;
use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::{self, WebGLContextAttributes};
use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding;
use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::TexImageSource;
use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLContextAttributes;
use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextMethods;
use dom::bindings::codegen::UnionTypes::ArrayBufferViewOrArrayBuffer;
use dom::bindings::codegen::UnionTypes::Float32ArrayOrUnrestrictedFloatSequence;
use dom::bindings::codegen::UnionTypes::ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement;
use dom::bindings::codegen::UnionTypes::Int32ArrayOrLongSequence;
use dom::bindings::conversions::{DerivedFrom, ToJSValConvertible};
use dom::bindings::error::{Error, ErrorResult};
use dom::bindings::error::{Error, ErrorResult, Fallible};
use dom::bindings::inheritance::Castable;
use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object};
use dom::bindings::root::{Dom, DomOnceCell, DomRoot, LayoutDom, MutNullableDom};
@ -30,7 +31,7 @@ use dom::event::{Event, EventBubbles, EventCancelable};
use dom::htmlcanvaselement::HTMLCanvasElement;
use dom::htmlcanvaselement::utils as canvas_utils;
use dom::htmliframeelement::HTMLIFrameElement;
use dom::node::{Node, NodeDamage, window_from_node};
use dom::node::{Node, NodeDamage, document_from_node, window_from_node};
use dom::webgl_extensions::WebGLExtensions;
use dom::webgl_validations::WebGLValidator;
use dom::webgl_validations::tex_image_2d::{CommonTexImage2DValidator, CommonTexImage2DValidatorResult};
@ -76,7 +77,6 @@ pub fn is_gles() -> bool {
cfg!(any(target_os = "android", target_os = "ios"))
}
type ImagePixelResult = Result<(Vec<u8>, Size2D<i32>, bool), ()>;
pub const MAX_UNIFORM_AND_ATTRIBUTE_LEN: usize = 256;
// From the GLES 2.0.25 spec, page 85:
@ -490,21 +490,21 @@ impl WebGLRenderingContext {
fn get_image_pixels(
&self,
source: ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement,
) -> ImagePixelResult {
// NOTE: Getting the pixels probably can be short-circuited if some
// parameter is invalid.
//
// Nontheless, since it's the error case, I'm not totally sure the
// complexity is worth it.
let (pixels, size, premultiplied) = match source {
ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement::ImageData(image_data) => {
source: TexImageSource,
) -> Fallible<Option<(Vec<u8>, Size2D<i32>, bool)>> {
Ok(Some(match source {
TexImageSource::ImageData(image_data) => {
(image_data.get_data_array(), image_data.get_size(), false)
},
ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement::HTMLImageElement(image) => {
TexImageSource::HTMLImageElement(image) => {
let document = document_from_node(&*self.canvas);
if !image.same_origin(document.origin()) {
return Err(Error::Security);
}
let img_url = match image.get_url() {
Some(url) => url,
None => return Err(()),
None => return Ok(None),
};
let window = window_from_node(&*self.canvas);
@ -513,7 +513,7 @@ impl WebGLRenderingContext {
ImageResponse::Loaded(img, _) => img,
ImageResponse::PlaceholderLoaded(_, _) | ImageResponse::None |
ImageResponse::MetadataLoaded(_)
=> return Err(()),
=> return Ok(None),
};
let size = Size2D::new(img.width as i32, img.height as i32);
@ -531,22 +531,23 @@ impl WebGLRenderingContext {
// TODO(emilio): Getting canvas data is implemented in CanvasRenderingContext2D,
// but we need to refactor it moving it to `HTMLCanvasElement` and support
// WebGLContext (probably via GetPixels()).
ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement::HTMLCanvasElement(canvas) => {
TexImageSource::HTMLCanvasElement(canvas) => {
if !canvas.origin_is_clean() {
return Err(Error::Security);
}
if let Some((mut data, size)) = canvas.fetch_all_data() {
// Pixels got from Canvas have already alpha premultiplied
byte_swap(&mut data);
(data, size, true)
} else {
return Err(());
return Ok(None);
}
},
ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement::HTMLVideoElement(_) => {
TexImageSource::HTMLVideoElement(_) => {
// TODO: https://github.com/servo/servo/issues/6711
return Err(());
return Ok(None);
}
};
return Ok((pixels, size, premultiplied));
}))
}
// TODO(emilio): Move this logic to a validator.
@ -1234,7 +1235,15 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
constants::UNPACK_ALIGNMENT => {
return UInt32Value(self.texture_unpacking_alignment.get());
},
_ => {}
constants::UNPACK_COLORSPACE_CONVERSION_WEBGL => {
let unpack = self.texture_unpacking_settings.get();
return UInt32Value(if unpack.contains(TextureUnpacking::CONVERT_COLORSPACE) {
constants::BROWSER_DEFAULT_WEBGL
} else {
constants::NONE
});
},
_ => {},
}
// Handle any MAX_ parameters by retrieving the limits that were stored
@ -3520,16 +3529,15 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
internal_format: u32,
format: u32,
data_type: u32,
source: ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement,
source: TexImageSource,
) -> ErrorResult {
if !self.extension_manager.is_tex_type_enabled(data_type) {
return Ok(self.webgl_error(InvalidEnum));
}
// Get pixels from image source
let (pixels, size, premultiplied) = match self.get_image_pixels(source) {
Ok((pixels, size, premultiplied)) => (pixels, size, premultiplied),
Err(_) => return Ok(()),
let (pixels, size, premultiplied) = match self.get_image_pixels(source)? {
Some(triple) => triple,
None => return Ok(()),
};
let validator = TexImage2DValidator::new(self,
@ -3613,7 +3621,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
height: i32,
format: u32,
data_type: u32,
mut pixels: CustomAutoRooterGuard<Option<ArrayBufferView>>,
pixels: CustomAutoRooterGuard<Option<ArrayBufferView>>,
) -> ErrorResult {
let validator = TexImage2DValidator::new(self, target, level,
format, width, height,
@ -3644,10 +3652,11 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
// If data is null, a buffer of sufficient size
// initialized to 0 is passed.
let buff = match *pixels {
None => vec![0u8; expected_byte_length as usize],
Some(ref mut data) => data.to_vec(),
};
let buff = handle_potential_webgl_error!(
self,
pixels.as_ref().map(|p| p.to_vec()).ok_or(InvalidValue),
return Ok(())
);
// From the WebGL spec:
//
@ -3677,11 +3686,11 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
yoffset: i32,
format: u32,
data_type: u32,
source: ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement,
source: TexImageSource,
) -> ErrorResult {
let (pixels, size, premultiplied) = match self.get_image_pixels(source) {
Ok((pixels, size, premultiplied)) => (pixels, size, premultiplied),
Err(_) => return Ok(()),
let (pixels, size, premultiplied) = match self.get_image_pixels(source)? {
Some(triple) => triple,
None => return Ok(()),
};
let validator = TexImage2DValidator::new(self, target, level, format,