Don't create 2D canvas contexts arbitrarily

Sometimes, the canvas still has no rendering context, in this case it represents
a transparent black rectangle.
This commit is contained in:
Anthony Ramine 2018-09-15 12:28:53 +02:00
parent ed673f8070
commit f1e8eb640c
4 changed files with 107 additions and 90 deletions

View file

@ -132,15 +132,21 @@ 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 {
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( self.canvas(canvas_id).draw_image(
imagedata.into(), data,
image_size, image_size,
dest_rect, dest_rect,
source_rect, source_rect,

View file

@ -38,7 +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), 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),

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;
@ -315,7 +315,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 +325,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 +340,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 +349,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 +358,40 @@ impl CanvasRenderingContext2D {
let smoothing_enabled = self.state.borrow().image_smoothing_enabled; let smoothing_enabled = self.state.borrow().image_smoothing_enabled;
if let Some(context) = canvas.context() {
match *context {
CanvasContext::Context2d(ref context) => {
if self.canvas.as_ref().map_or(false, |c| &**c == canvas) { if self.canvas.as_ref().map_or(false, |c| &**c == canvas) {
self.send_canvas_2d_msg(Canvas2dMsg::DrawImageSelf( self.send_canvas_2d_msg(Canvas2dMsg::DrawImageSelf(
image_size, dest_rect, source_rect, smoothing_enabled)); image_size,
dest_rect,
source_rect,
smoothing_enabled,
));
} else { } else {
let context = match canvas.get_or_init_2d_context() { context.get_ipc_renderer().send(CanvasMsg::Canvas2d(
Some(context) => context,
None => return Err(Error::InvalidState),
};
let msg = CanvasMsg::Canvas2d(
Canvas2dMsg::DrawImageInOther( 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() context.get_canvas_id(),
); )).unwrap();
}
let renderer = context.get_ipc_renderer(); },
renderer.send(msg).unwrap(); _ => return Err(Error::InvalidState),
}; }
} else {
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 +461,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 +1219,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,18 +175,22 @@ 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() {
CanvasContext::Context2d(ref context) => Some(DomRoot::from_ref(&*context)),
_ => None,
}
} }
#[allow(unsafe_code)] #[allow(unsafe_code)]
@ -194,20 +199,18 @@ impl HTMLCanvasElement {
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() {
Some(DomRoot::from_ref(&*context))
} else {
None
}
} }
#[allow(unsafe_code)] #[allow(unsafe_code)]
@ -219,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.
@ -289,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))