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

10
Cargo.lock generated
View file

@ -109,8 +109,8 @@ dependencies = [
[[package]] [[package]]
name = "azure" name = "azure"
version = "0.33.0" version = "0.34.0"
source = "git+https://github.com/servo/rust-azure#43e61bda11f26e8462cfa63affac17c43df53b84" source = "git+https://github.com/servo/rust-azure#1536ca66954356ece017343e3cd30b293134dc3f"
dependencies = [ dependencies = [
"cmake 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "cmake 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -324,7 +324,7 @@ dependencies = [
name = "canvas" name = "canvas"
version = "0.0.1" version = "0.0.1"
dependencies = [ dependencies = [
"azure 0.33.0 (git+https://github.com/servo/rust-azure)", "azure 0.34.0 (git+https://github.com/servo/rust-azure)",
"canvas_traits 0.0.1", "canvas_traits 0.0.1",
"compositing 0.0.1", "compositing 0.0.1",
"cssparser 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)", "cssparser 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2851,7 +2851,7 @@ dependencies = [
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -4335,7 +4335,7 @@ dependencies = [
"checksum atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fb2dcb6e6d35f20276943cc04bb98e538b348d525a04ac79c10021561d202f21" "checksum atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fb2dcb6e6d35f20276943cc04bb98e538b348d525a04ac79c10021561d202f21"
"checksum atty 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "21e50800ec991574876040fff8ee46b136a53e985286fbe6a3bdfe6421b78860" "checksum atty 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "21e50800ec991574876040fff8ee46b136a53e985286fbe6a3bdfe6421b78860"
"checksum audio-video-metadata 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "3c23600291b35b9cd381f5cfca636f5d7d75e7d7192eddf867de84a6732cad5c" "checksum audio-video-metadata 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "3c23600291b35b9cd381f5cfca636f5d7d75e7d7192eddf867de84a6732cad5c"
"checksum azure 0.33.0 (git+https://github.com/servo/rust-azure)" = "<none>" "checksum azure 0.34.0 (git+https://github.com/servo/rust-azure)" = "<none>"
"checksum backtrace 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "72f9b4182546f4b04ebc4ab7f84948953a118bd6021a1b6a6c909e3e94f6be76" "checksum backtrace 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "72f9b4182546f4b04ebc4ab7f84948953a118bd6021a1b6a6c909e3e94f6be76"
"checksum backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "44585761d6161b0f57afc49482ab6bd067e4edef48c12a152c237eb0203f7661" "checksum backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "44585761d6161b0f57afc49482ab6bd067e4edef48c12a152c237eb0203f7661"
"checksum base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96434f987501f0ed4eb336a411e0631ecd1afa11574fe148587adc4ff96143c9" "checksum base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96434f987501f0ed4eb336a411e0631ecd1afa11574fe148587adc4ff96143c9"

View file

@ -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) { pub fn save_context_state(&mut self) {
self.saved_states.push(self.state.clone()); self.saved_states.push(self.state.clone());
} }
@ -405,14 +390,14 @@ impl<'a> CanvasData<'a> {
} }
} }
#[allow(unsafe_code)]
pub fn send_pixels(&mut self, chan: IpcSender<Option<ByteBuf>>) { pub fn send_pixels(&mut self, chan: IpcSender<Option<ByteBuf>>) {
self.drawtarget.snapshot().get_data_surface().with_data(|element| { let data = unsafe { self.drawtarget.snapshot().get_data_surface().data().to_vec() };
chan.send(Some(Vec::from(element).into())).unwrap(); chan.send(Some(data.into())).unwrap();
})
} }
#[allow(unsafe_code)]
pub fn send_data(&mut self, chan: IpcSender<CanvasImageData>) { 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 size = self.drawtarget.get_size();
let descriptor = webrender_api::ImageDescriptor { let descriptor = webrender_api::ImageDescriptor {
@ -423,7 +408,9 @@ impl<'a> CanvasData<'a> {
is_opaque: false, is_opaque: false,
allow_mipmaps: false, allow_mipmaps: false,
}; };
let data = webrender_api::ImageData::Raw(Arc::new(element.into())); let data = webrender_api::ImageData::Raw(Arc::new(
unsafe { self.drawtarget.snapshot().get_data_surface().data().into() },
));
let mut txn = webrender_api::Transaction::new(); let mut txn = webrender_api::Transaction::new();
@ -449,7 +436,6 @@ impl<'a> CanvasData<'a> {
image_key: self.image_key.unwrap(), image_key: self.image_key.unwrap(),
}; };
chan.send(data).unwrap(); chan.send(data).unwrap();
})
} }
pub fn image_data( pub fn image_data(
@ -606,23 +592,25 @@ impl<'a> CanvasData<'a> {
/// It reads image data from the canvas /// It reads image data from the canvas
/// canvas_size: The size of the canvas we're reading from /// canvas_size: The size of the canvas we're reading from
/// read_rect: The area of the canvas we want to read 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> { pub fn read_pixels(&self, read_rect: Rect<i32>, canvas_size: Size2D<f64>) -> Vec<u8> {
let canvas_size = canvas_size.to_i32(); let canvas_size = canvas_size.to_i32();
let canvas_rect = Rect::new(Point2D::new(0i32, 0i32), canvas_size); 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 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 { 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 data_surface = self.drawtarget.snapshot().get_data_surface();
let mut src_data = Vec::new(); let src_data = unsafe { data_surface.data() };
data_surface.with_data(|element| { src_data = element.to_vec(); });
let stride = data_surface.stride(); let stride = data_surface.stride();
//start offset of the copyable rectangle //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 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 //copy the data to the destination vector
for _ in 0..src_read_rect.size.height { for _ in 0..src_read_rect.size.height {
let row = &src_data[src .. src + (4 * src_read_rect.size.width) as usize]; let row = &src_data[src .. src + (4 * src_read_rect.size.width) as usize];

View file

@ -132,32 +132,25 @@ impl<'a> CanvasPaintThread <'a> {
self.canvas(canvas_id).is_point_in_path(x, y, fill_rule, chan) self.canvas(canvas_id).is_point_in_path(x, y, fill_rule, chan)
}, },
Canvas2dMsg::DrawImage( Canvas2dMsg::DrawImage(
mut imagedata, imagedata,
image_size, image_size,
dest_rect, dest_rect,
source_rect, source_rect,
smoothing_enabled, smoothing_enabled,
) => { ) => {
byte_swap(&mut imagedata); let data = match imagedata {
self.canvas(canvas_id).draw_image( None => vec![0; image_size.width as usize * image_size.height as usize * 4],
imagedata.into(), Some(mut data) => {
image_size, byte_swap(&mut data);
dest_rect, data.into()
source_rect,
smoothing_enabled,
)
}, },
Canvas2dMsg::DrawImageSelf( };
self.canvas(canvas_id).draw_image(
data,
image_size, image_size,
dest_rect, dest_rect,
source_rect, source_rect,
smoothing_enabled smoothing_enabled,
) => {
self.canvas(canvas_id).draw_image_self(
image_size,
dest_rect,
source_rect,
smoothing_enabled
) )
}, },
Canvas2dMsg::DrawImageInOther( Canvas2dMsg::DrawImageInOther(
@ -167,9 +160,10 @@ impl<'a> CanvasPaintThread <'a> {
source_rect, source_rect,
smoothing smoothing
) => { ) => {
let mut image_data = self.canvas(canvas_id).read_pixels( let image_data = self.canvas(canvas_id).read_pixels(
source_rect.to_i32(), source_rect.to_i32(),
image_size); image_size,
);
self.canvas(other_canvas_id).draw_image( self.canvas(other_canvas_id).draw_image(
image_data.into(), image_data.into(),
source_rect.size, source_rect.size,

View file

@ -38,8 +38,7 @@ pub struct CanvasImageData {
pub enum Canvas2dMsg { pub enum Canvas2dMsg {
Arc(Point2D<f32>, f32, f32, f32, bool), Arc(Point2D<f32>, f32, f32, f32, bool),
ArcTo(Point2D<f32>, Point2D<f32>, f32), ArcTo(Point2D<f32>, Point2D<f32>, f32),
DrawImage(ByteBuf, Size2D<f64>, Rect<f64>, Rect<f64>, bool), DrawImage(Option<ByteBuf>, Size2D<f64>, Rect<f64>, Rect<f64>, bool),
DrawImageSelf(Size2D<f64>, Rect<f64>, Rect<f64>, bool),
DrawImageInOther( DrawImageInOther(
CanvasId, Size2D<f64>, Rect<f64>, Rect<f64>, bool), CanvasId, Size2D<f64>, Rect<f64>, Rect<f64>, bool),
BeginPath, BeginPath,

View file

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

View file

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

View file

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

View file

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

View file

@ -1,5 +0,0 @@
[security.dataURI.html]
type: testharness
[data: URIs do not count as different-origin, and do not taint the canvas]
expected: FAIL

View file

@ -1,5 +0,0 @@
[toDataURL.jpeg.primarycolours.html]
type: testharness
[toDataURL with JPEG handles simple colours correctly]
expected: FAIL

View file

@ -1,5 +0,0 @@
[toDataURL.png.complexcolours.html]
type: testharness
[toDataURL with PNG handles non-primary and non-solid colours correctly]
expected: FAIL

View file

@ -1,5 +0,0 @@
[toDataURL.png.primarycolours.html]
type: testharness
[toDataURL with PNG handles simple colours correctly]
expected: FAIL

View file

@ -1,5 +0,0 @@
[data-url.html]
type: testharness
[data URL image]
expected: FAIL

View file

@ -1,3 +1,91 @@
[to-data-url-test.html] [to-data-url-test.html]
bug: https://github.com/servo/servo/issues/21132 [WebGL test #12: should be 0,255,0,255\nat (0, 0) expected: 0,255,0,255 was 255,0,0,255]
expected: ERROR expected: FAIL
[WebGL test #18: should be 0,255,0,255\nat (0, 0) expected: 0,255,0,255 was 255,0,0,255]
expected: FAIL
[WebGL test #1: should be 255,0,0,255\nat (0, 8) expected: 255,0,0,255 was 0,255,0,255]
expected: FAIL
[WebGL test #4: should be 255,0,0,255\nat (0, 8) expected: 255,0,0,255 was 0,255,0,255]
expected: FAIL
[WebGL test #34: should be 255,0,0,255\nat (0, 256) expected: 255,0,0,255 was 0,255,0,255]
expected: FAIL
[WebGL test #24: should be 0,255,0,255\nat (0, 0) expected: 0,255,0,255 was 255,0,0,255]
expected: FAIL
[WebGL test #31: should be 255,0,0,255\nat (0, 256) expected: 255,0,0,255 was 0,255,0,255]
expected: FAIL
[WebGL test #10: should be 255,0,0,255\nat (0, 8) expected: 255,0,0,255 was 0,255,0,255]
expected: FAIL
[WebGL test #25: should be 255,0,0,255\nat (0, 128) expected: 255,0,0,255 was 0,255,0,255]
expected: FAIL
[WebGL test #40: should be 255,0,0,255\nat (0, 256) expected: 255,0,0,255 was 0,255,0,255]
expected: FAIL
[WebGL test #19: should be 255,0,0,255\nat (0, 128) expected: 255,0,0,255 was 0,255,0,255]
expected: FAIL
[WebGL test #9: should be 0,255,0,255\nat (0, 0) expected: 0,255,0,255 was 255,0,0,255]
expected: FAIL
[WebGL test #3: should be 0,255,0,255\nat (0, 0) expected: 0,255,0,255 was 255,0,0,255]
expected: FAIL
[WebGL test #43: should be 255,0,0,255\nat (0, 257) expected: 255,0,0,255 was 0,255,0,255]
expected: FAIL
[WebGL test #15: should be 0,255,0,255\nat (0, 0) expected: 0,255,0,255 was 255,0,0,255]
expected: FAIL
[WebGL test #16: should be 255,0,0,255\nat (0, 128) expected: 255,0,0,255 was 0,255,0,255]
expected: FAIL
[WebGL test #21: should be 0,255,0,255\nat (0, 0) expected: 0,255,0,255 was 255,0,0,255]
expected: FAIL
[WebGL test #33: should be 0,255,0,255\nat (0, 0) expected: 0,255,0,255 was 255,0,0,255]
expected: FAIL
[WebGL test #0: should be 0,255,0,255\nat (0, 0) expected: 0,255,0,255 was 255,0,0,255]
expected: FAIL
[WebGL test #30: should be 0,255,0,255\nat (0, 0) expected: 0,255,0,255 was 255,0,0,255]
expected: FAIL
[WebGL test #22: should be 255,0,0,255\nat (0, 128) expected: 255,0,0,255 was 0,255,0,255]
expected: FAIL
[WebGL test #39: should be 0,255,0,255\nat (0, 0) expected: 0,255,0,255 was 255,0,0,255]
expected: FAIL
[WebGL test #28: should be 255,0,0,255\nat (0, 129) expected: 255,0,0,255 was 0,255,0,255]
expected: FAIL
[WebGL test #7: should be 255,0,0,255\nat (0, 8) expected: 255,0,0,255 was 0,255,0,255]
expected: FAIL
[WebGL test #36: should be 0,255,0,255\nat (0, 0) expected: 0,255,0,255 was 255,0,0,255]
expected: FAIL
[WebGL test #42: should be 0,255,0,255\nat (0, 0) expected: 0,255,0,255 was 255,0,0,255]
expected: FAIL
[WebGL test #27: should be 0,255,0,255\nat (0, 0) expected: 0,255,0,255 was 255,0,0,255]
expected: FAIL
[WebGL test #13: should be 255,0,0,255\nat (0, 9) expected: 255,0,0,255 was 0,255,0,255]
expected: FAIL
[WebGL test #37: should be 255,0,0,255\nat (0, 256) expected: 255,0,0,255 was 0,255,0,255]
expected: FAIL
[WebGL test #6: should be 0,255,0,255\nat (0, 0) expected: 0,255,0,255 was 255,0,0,255]
expected: FAIL

View file

@ -1,11 +0,0 @@
[webgl-specific.html]
bug: https://github.com/servo/servo/issues/20552
[WebGL test #25: getError expected: NO_ERROR. Was INVALID_ENUM : set/get UNPACK_COLORSPACE_CONVERSION_WEBGL should generate no error]
expected: FAIL
[WebGL test #23: gl.getParameter(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL) should be 37444 (of type number). Was null (of type object).]
expected: FAIL
[WebGL test #24: gl.getParameter(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL) should be 0 (of type number). Was null (of type object).]
expected: FAIL

View file

@ -1,8 +0,0 @@
[readPixelsBadArgs.html]
bug: https://github.com/servo/servo/issues/21522
[WebGL test #1: testReadPixelsSOPIMG]
expected: FAIL
[WebGL test #2: testReadPixelsSOPCanvas]
expected: FAIL

View file

@ -1,5 +0,0 @@
[texImage2DHTML.html]
bug: https://github.com/servo/servo/issues/21522
[WebGL test #1: testTexImage2DNonSOP]
expected: FAIL

View file

@ -1,5 +0,0 @@
[texSubImage2DHTML.html]
bug: https://github.com/servo/servo/issues/21522
[WebGL test #1: testTexImage2DNonSOP]
expected: FAIL

View file

@ -1,13 +0,0 @@
[origin-clean-conformance.html]
[WebGL test #3: texSubImage2D with cross-origin image should throw exception.]
expected: FAIL
[WebGL test #8: texSubImage2D with NON origin clean canvas should throw exception.]
expected: FAIL
[WebGL test #2: texImage2D with cross-origin image should throw exception.]
expected: FAIL
[WebGL test #7: texImage2D with NON origin clean canvas should throw exception.]
expected: FAIL

View file

@ -11,6 +11,3 @@
[WebGL test #34: getError expected: INVALID_OPERATION. Was NO_ERROR : colorBufferFormat: RGB565 internalFormat: RGBA target: TEXTURE_2D border: 0] [WebGL test #34: getError expected: INVALID_OPERATION. Was NO_ERROR : colorBufferFormat: RGB565 internalFormat: RGBA target: TEXTURE_2D border: 0]
expected: FAIL expected: FAIL
[WebGL test #37: getError expected: NO_ERROR. Was INVALID_OPERATION : colorBufferFormat: RGB565 internalFormat: RGB target: TEXTURE_2D border: 0]
expected: FAIL

View file

@ -1,7 +1,4 @@
[tex-sub-image-2d-bad-args.html] [tex-sub-image-2d-bad-args.html]
[WebGL test #9: getError expected: INVALID_VALUE. Was NO_ERROR : null pixels]
expected: FAIL
[WebGL test #0: getError expected: INVALID_OPERATION. Was INVALID_VALUE : no previously defined texture image] [WebGL test #0: getError expected: INVALID_OPERATION. Was INVALID_VALUE : no previously defined texture image]
expected: FAIL expected: FAIL

View file

@ -1,7 +0,0 @@
[texture-attachment-formats.html]
[WebGL test #14: should be 63,63,63,255 with tolerance 2,2,2,0\nat (0, 0) expected: 63,63,63,255 was 64,0,0,255]
expected: FAIL
[WebGL test #16: should be 63,63,63,63 with tolerance 2,2,2,2\nat (0, 0) expected: 63,63,63,63 was 64,0,0,64]
expected: FAIL