diff options
author | bors-servo <lbergstrom+bors@mozilla.com> | 2018-09-18 11:06:51 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-09-18 11:06:51 -0400 |
commit | 82e94e3b678f2638d62d1ced613f0555805b24e3 (patch) | |
tree | 39fb1f7e9f5fb6764274c30603ad6b79e848abb9 /components | |
parent | bdf450336e9c16cb5f6e0d462f866f787dc66e7f (diff) | |
parent | 900c3cc6b526616d35994fda526e7166f13cd12d (diff) | |
download | servo-82e94e3b678f2638d62d1ced613f0555805b24e3.tar.gz servo-82e94e3b678f2638d62d1ced613f0555805b24e3.zip |
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 -->
Diffstat (limited to 'components')
-rw-r--r-- | components/canvas/canvas_data.rs | 100 | ||||
-rw-r--r-- | components/canvas/canvas_paint_thread.rs | 32 | ||||
-rw-r--r-- | components/canvas_traits/canvas.rs | 3 | ||||
-rw-r--r-- | components/script/dom/canvasrenderingcontext2d.rs | 95 | ||||
-rw-r--r-- | components/script/dom/htmlcanvaselement.rs | 94 | ||||
-rw-r--r-- | components/script/dom/htmlimageelement.rs | 11 | ||||
-rw-r--r-- | components/script/dom/webglrenderingcontext.rs | 87 |
7 files changed, 207 insertions, 215 deletions
diff --git a/components/canvas/canvas_data.rs b/components/canvas/canvas_data.rs index 492a8da150a..b1768fe8744 100644 --- a/components/canvas/canvas_data.rs +++ b/components/canvas/canvas_data.rs @@ -85,21 +85,6 @@ impl<'a> CanvasData<'a> { } } - pub fn draw_image_self( - &self, - image_size: Size2D<f64>, - dest_rect: Rect<f64>, - source_rect: Rect<f64>, - smoothing_enabled: bool - ) { - // Reads pixels from source image - // In this case source and target are the same canvas - let image_data = self.read_pixels(source_rect.to_i32(), image_size); - - // The dimensions of image_data are source_rect.size - self.draw_image(image_data, source_rect.size, dest_rect, source_rect, smoothing_enabled); - } - pub fn save_context_state(&mut self) { self.saved_states.push(self.state.clone()); } @@ -405,51 +390,52 @@ impl<'a> CanvasData<'a> { } } + #[allow(unsafe_code)] pub fn send_pixels(&mut self, chan: IpcSender<Option<ByteBuf>>) { - self.drawtarget.snapshot().get_data_surface().with_data(|element| { - chan.send(Some(Vec::from(element).into())).unwrap(); - }) + let data = unsafe { self.drawtarget.snapshot().get_data_surface().data().to_vec() }; + chan.send(Some(data.into())).unwrap(); } + #[allow(unsafe_code)] pub fn send_data(&mut self, chan: IpcSender<CanvasImageData>) { - self.drawtarget.snapshot().get_data_surface().with_data(|element| { - let size = self.drawtarget.get_size(); - - let descriptor = webrender_api::ImageDescriptor { - size: webrender_api::DeviceUintSize::new(size.width as u32, size.height as u32), - stride: None, - format: webrender_api::ImageFormat::BGRA8, - offset: 0, - is_opaque: false, - allow_mipmaps: false, - }; - let data = webrender_api::ImageData::Raw(Arc::new(element.into())); - - let mut txn = webrender_api::Transaction::new(); - - match self.image_key { - Some(image_key) => { - debug!("Updating image {:?}.", image_key); - txn.update_image(image_key, descriptor, data, None); - } - None => { - self.image_key = Some(self.webrender_api.generate_image_key()); - debug!("New image {:?}.", self.image_key); - txn.add_image(self.image_key.unwrap(), descriptor, data, None); - } - } + let size = self.drawtarget.get_size(); + + let descriptor = webrender_api::ImageDescriptor { + size: webrender_api::DeviceUintSize::new(size.width as u32, size.height as u32), + stride: None, + format: webrender_api::ImageFormat::BGRA8, + offset: 0, + is_opaque: false, + allow_mipmaps: false, + }; + let data = webrender_api::ImageData::Raw(Arc::new( + unsafe { self.drawtarget.snapshot().get_data_surface().data().into() }, + )); - if let Some(image_key) = mem::replace(&mut self.very_old_image_key, self.old_image_key.take()) { - txn.delete_image(image_key); + let mut txn = webrender_api::Transaction::new(); + + match self.image_key { + Some(image_key) => { + debug!("Updating image {:?}.", image_key); + txn.update_image(image_key, descriptor, data, None); + } + None => { + self.image_key = Some(self.webrender_api.generate_image_key()); + debug!("New image {:?}.", self.image_key); + txn.add_image(self.image_key.unwrap(), descriptor, data, None); } + } - self.webrender_api.update_resources(txn.resource_updates); + if let Some(image_key) = mem::replace(&mut self.very_old_image_key, self.old_image_key.take()) { + txn.delete_image(image_key); + } - let data = CanvasImageData { - image_key: self.image_key.unwrap(), - }; - chan.send(data).unwrap(); - }) + self.webrender_api.update_resources(txn.resource_updates); + + let data = CanvasImageData { + image_key: self.image_key.unwrap(), + }; + chan.send(data).unwrap(); } pub fn image_data( @@ -606,23 +592,25 @@ impl<'a> CanvasData<'a> { /// It reads image data from the canvas /// canvas_size: The size of the canvas we're reading from /// read_rect: The area of the canvas we want to read from + #[allow(unsafe_code)] pub fn read_pixels(&self, read_rect: Rect<i32>, canvas_size: Size2D<f64>) -> Vec<u8> { let canvas_size = canvas_size.to_i32(); let canvas_rect = Rect::new(Point2D::new(0i32, 0i32), canvas_size); let src_read_rect = canvas_rect.intersection(&read_rect).unwrap_or(Rect::zero()); - let mut image_data = vec![]; if src_read_rect.is_empty() || canvas_size.width <= 0 && canvas_size.height <= 0 { - return image_data; + return vec![]; } let data_surface = self.drawtarget.snapshot().get_data_surface(); - let mut src_data = Vec::new(); - data_surface.with_data(|element| { src_data = element.to_vec(); }); + let src_data = unsafe { data_surface.data() }; let stride = data_surface.stride(); //start offset of the copyable rectangle let mut src = (src_read_rect.origin.y * stride + src_read_rect.origin.x * 4) as usize; + let mut image_data = Vec::with_capacity( + (src_read_rect.size.width * src_read_rect.size.height * 4) as usize, + ); //copy the data to the destination vector for _ in 0..src_read_rect.size.height { let row = &src_data[src .. src + (4 * src_read_rect.size.width) as usize]; diff --git a/components/canvas/canvas_paint_thread.rs b/components/canvas/canvas_paint_thread.rs index 7f0130e8f3e..3e66194e94c 100644 --- a/components/canvas/canvas_paint_thread.rs +++ b/components/canvas/canvas_paint_thread.rs @@ -132,34 +132,27 @@ impl<'a> CanvasPaintThread <'a> { self.canvas(canvas_id).is_point_in_path(x, y, fill_rule, chan) }, Canvas2dMsg::DrawImage( - mut imagedata, + imagedata, image_size, dest_rect, source_rect, smoothing_enabled, ) => { - byte_swap(&mut imagedata); + let data = match imagedata { + None => vec![0; image_size.width as usize * image_size.height as usize * 4], + Some(mut data) => { + byte_swap(&mut data); + data.into() + }, + }; self.canvas(canvas_id).draw_image( - imagedata.into(), + data, image_size, dest_rect, source_rect, smoothing_enabled, ) }, - Canvas2dMsg::DrawImageSelf( - image_size, - dest_rect, - source_rect, - smoothing_enabled - ) => { - self.canvas(canvas_id).draw_image_self( - image_size, - dest_rect, - source_rect, - smoothing_enabled - ) - }, Canvas2dMsg::DrawImageInOther( other_canvas_id, image_size, @@ -167,9 +160,10 @@ impl<'a> CanvasPaintThread <'a> { source_rect, smoothing ) => { - let mut image_data = self.canvas(canvas_id).read_pixels( - source_rect.to_i32(), - image_size); + let image_data = self.canvas(canvas_id).read_pixels( + source_rect.to_i32(), + image_size, + ); self.canvas(other_canvas_id).draw_image( image_data.into(), source_rect.size, diff --git a/components/canvas_traits/canvas.rs b/components/canvas_traits/canvas.rs index 661bae6b606..a9d47d252a4 100644 --- a/components/canvas_traits/canvas.rs +++ b/components/canvas_traits/canvas.rs @@ -38,8 +38,7 @@ pub struct CanvasImageData { pub enum Canvas2dMsg { Arc(Point2D<f32>, f32, f32, f32, bool), ArcTo(Point2D<f32>, Point2D<f32>, f32), - DrawImage(ByteBuf, Size2D<f64>, Rect<f64>, Rect<f64>, bool), - DrawImageSelf(Size2D<f64>, Rect<f64>, Rect<f64>, bool), + DrawImage(Option<ByteBuf>, Size2D<f64>, Rect<f64>, Rect<f64>, bool), DrawImageInOther( CanvasId, Size2D<f64>, Rect<f64>, Rect<f64>, bool), BeginPath, diff --git a/components/script/dom/canvasrenderingcontext2d.rs b/components/script/dom/canvasrenderingcontext2d.rs index 24d6af0282d..948fd0b9efe 100644 --- a/components/script/dom/canvasrenderingcontext2d.rs +++ b/components/script/dom/canvasrenderingcontext2d.rs @@ -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) => { diff --git a/components/script/dom/htmlcanvaselement.rs b/components/script/dom/htmlcanvaselement.rs index cfc1ee9a3db..7bc6a233e66 100644 --- a/components/script/dom/htmlcanvaselement.rs +++ b/components/script/dom/htmlcanvaselement.rs @@ -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))); - } + pub fn context(&self) -> Option<Ref<CanvasContext>> { + ref_filter_map::ref_filter_map(self.context.borrow(), |ctx| ctx.as_ref()) + } - match *self.context.borrow().as_ref().unwrap() { - CanvasContext::Context2d(ref context) => Some(DomRoot::from_ref(&*context)), - _ => None, + 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) } - pub fn get_or_init_webgl_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)) diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs index 1c49185ad0d..b665014cdca 100644 --- a/components/script/dom/htmlimageelement.rs +++ b/components/script/dom/htmlimageelement.rs @@ -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) + }) } } diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs index 2f3df2adb7c..fadfb42f668 100644 --- a/components/script/dom/webglrenderingcontext.rs +++ b/components/script/dom/webglrenderingcontext.rs @@ -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, |