Check for valid arguments before processing canvas.fillText

This commit is contained in:
Utsav Oza 2020-05-22 15:51:36 +05:30
parent 68f641ee78
commit 726f7d7209
4 changed files with 44 additions and 38 deletions

1
Cargo.lock generated
View file

@ -504,6 +504,7 @@ dependencies = [
"cssparser", "cssparser",
"euclid", "euclid",
"fnv", "fnv",
"font-kit",
"gleam 0.11.0", "gleam 0.11.0",
"half", "half",
"ipc-channel", "ipc-channel",

View file

@ -22,6 +22,7 @@ canvas_traits = { path = "../canvas_traits" }
crossbeam-channel = "0.4" crossbeam-channel = "0.4"
cssparser = "0.27" cssparser = "0.27"
euclid = "0.20" euclid = "0.20"
font-kit = "0.7"
fnv = "1.0" fnv = "1.0"
gleam = "0.11" gleam = "0.11"
half = "1" half = "1"
@ -30,7 +31,7 @@ log = "0.4"
lyon_geom = "0.14" lyon_geom = "0.14"
num-traits = "0.2" num-traits = "0.2"
pixels = { path = "../pixels" } pixels = { path = "../pixels" }
raqote = "0.8" raqote = { version = "0.8", features = ["text"] }
servo_config = { path = "../config" } servo_config = { path = "../config" }
sparkle = "0.1.24" sparkle = "0.1.24"
# NOTE: the sm-angle feature only enables ANGLE on Windows, not other platforms! # NOTE: the sm-angle feature only enables ANGLE on Windows, not other platforms!

View file

@ -362,7 +362,7 @@ pub enum Filter {
pub struct CanvasData<'a> { pub struct CanvasData<'a> {
backend: Box<dyn Backend>, backend: Box<dyn Backend>,
drawtarget: Box<dyn GenericDrawTarget>, draw_target: Box<dyn GenericDrawTarget>,
path_state: Option<PathState>, path_state: Option<PathState>,
state: CanvasPaintState<'a>, state: CanvasPaintState<'a>,
saved_states: Vec<CanvasPaintState<'a>>, saved_states: Vec<CanvasPaintState<'a>>,
@ -372,7 +372,7 @@ pub struct CanvasData<'a> {
old_image_key: Option<webrender_api::ImageKey>, old_image_key: Option<webrender_api::ImageKey>,
/// An old webrender image key that can be deleted when the current epoch ends. /// An old webrender image key that can be deleted when the current epoch ends.
very_old_image_key: Option<webrender_api::ImageKey>, very_old_image_key: Option<webrender_api::ImageKey>,
pub canvas_id: CanvasId, _canvas_id: CanvasId,
} }
fn create_backend() -> Box<dyn Backend> { fn create_backend() -> Box<dyn Backend> {
@ -390,7 +390,7 @@ impl<'a> CanvasData<'a> {
let draw_target = backend.create_drawtarget(size); let draw_target = backend.create_drawtarget(size);
CanvasData { CanvasData {
backend, backend,
drawtarget: draw_target, draw_target,
path_state: None, path_state: None,
state: CanvasPaintState::new(antialias), state: CanvasPaintState::new(antialias),
saved_states: vec![], saved_states: vec![],
@ -398,7 +398,7 @@ impl<'a> CanvasData<'a> {
image_key: None, image_key: None,
old_image_key: None, old_image_key: None,
very_old_image_key: None, very_old_image_key: None,
canvas_id: canvas_id, _canvas_id: canvas_id,
} }
} }
@ -440,7 +440,7 @@ impl<'a> CanvasData<'a> {
// TODO(pylbrecht) pass another closure for raqote // TODO(pylbrecht) pass another closure for raqote
self.draw_with_shadow(&rect, writer); self.draw_with_shadow(&rect, writer);
} else { } else {
writer(&mut *self.drawtarget); writer(&mut *self.draw_target);
} }
} }
@ -451,8 +451,8 @@ impl<'a> CanvasData<'a> {
pub fn restore_context_state(&mut self) { pub fn restore_context_state(&mut self) {
if let Some(state) = self.saved_states.pop() { if let Some(state) = self.saved_states.pop() {
let _ = mem::replace(&mut self.state, state); let _ = mem::replace(&mut self.state, state);
self.drawtarget.set_transform(&self.state.transform); self.draw_target.set_transform(&self.state.transform);
self.drawtarget.pop_clip(); self.draw_target.pop_clip();
} }
} }
@ -513,7 +513,7 @@ impl<'a> CanvasData<'a> {
); );
}); });
} else { } else {
self.drawtarget.fill_rect( self.draw_target.fill_rect(
&draw_rect, &draw_rect,
self.state.fill_style.clone(), self.state.fill_style.clone(),
Some(&self.state.draw_options), Some(&self.state.draw_options),
@ -522,7 +522,7 @@ impl<'a> CanvasData<'a> {
} }
pub fn clear_rect(&mut self, rect: &Rect<f32>) { pub fn clear_rect(&mut self, rect: &Rect<f32>) {
self.drawtarget.clear_rect(rect); self.draw_target.clear_rect(rect);
} }
pub fn stroke_rect(&mut self, rect: &Rect<f32>) { pub fn stroke_rect(&mut self, rect: &Rect<f32>) {
@ -542,7 +542,7 @@ impl<'a> CanvasData<'a> {
} else if rect.size.width == 0. || rect.size.height == 0. { } else if rect.size.width == 0. || rect.size.height == 0. {
let mut stroke_opts = self.state.stroke_opts.clone(); let mut stroke_opts = self.state.stroke_opts.clone();
stroke_opts.set_line_cap(LineCapStyle::Butt); stroke_opts.set_line_cap(LineCapStyle::Butt);
self.drawtarget.stroke_line( self.draw_target.stroke_line(
rect.origin, rect.origin,
rect.bottom_right(), rect.bottom_right(),
self.state.stroke_style.clone(), self.state.stroke_style.clone(),
@ -550,7 +550,7 @@ impl<'a> CanvasData<'a> {
&self.state.draw_options, &self.state.draw_options,
); );
} else { } else {
self.drawtarget.stroke_rect( self.draw_target.stroke_rect(
rect, rect,
self.state.stroke_style.clone(), self.state.stroke_style.clone(),
&self.state.stroke_opts, &self.state.stroke_opts,
@ -572,7 +572,7 @@ impl<'a> CanvasData<'a> {
// If there's no record of any path yet, create a new builder in user-space. // If there's no record of any path yet, create a new builder in user-space.
if self.path_state.is_none() { if self.path_state.is_none() {
self.path_state = Some(PathState::UserSpacePathBuilder( self.path_state = Some(PathState::UserSpacePathBuilder(
self.drawtarget.create_path_builder(), self.draw_target.create_path_builder(),
None, None,
)); ));
} }
@ -607,7 +607,7 @@ impl<'a> CanvasData<'a> {
let new_state = match *self.path_state.as_mut().unwrap() { let new_state = match *self.path_state.as_mut().unwrap() {
PathState::DeviceSpacePathBuilder(ref mut builder) => { PathState::DeviceSpacePathBuilder(ref mut builder) => {
let path = builder.finish(); let path = builder.finish();
let inverse = match self.drawtarget.get_transform().inverse() { let inverse = match self.draw_target.get_transform().inverse() {
Some(m) => m, Some(m) => m,
None => { None => {
warn!("Couldn't invert canvas transformation."); warn!("Couldn't invert canvas transformation.");
@ -639,7 +639,7 @@ impl<'a> CanvasData<'a> {
} }
self.ensure_path(); self.ensure_path();
self.drawtarget.fill( self.draw_target.fill(
&self.path().clone(), &self.path().clone(),
self.state.fill_style.clone(), self.state.fill_style.clone(),
&self.state.draw_options, &self.state.draw_options,
@ -652,7 +652,7 @@ impl<'a> CanvasData<'a> {
} }
self.ensure_path(); self.ensure_path();
self.drawtarget.stroke( self.draw_target.stroke(
&self.path().clone(), &self.path().clone(),
self.state.stroke_style.clone(), self.state.stroke_style.clone(),
&self.state.stroke_opts, &self.state.stroke_opts,
@ -663,7 +663,7 @@ impl<'a> CanvasData<'a> {
pub fn clip(&mut self) { pub fn clip(&mut self) {
self.ensure_path(); self.ensure_path();
let path = self.path().clone(); let path = self.path().clone();
self.drawtarget.push_clip(&path); self.draw_target.push_clip(&path);
} }
pub fn is_point_in_path( pub fn is_point_in_path(
@ -676,7 +676,7 @@ impl<'a> CanvasData<'a> {
self.ensure_path(); self.ensure_path();
let result = match self.path_state.as_ref() { let result = match self.path_state.as_ref() {
Some(PathState::UserSpacePath(ref path, ref transform)) => { Some(PathState::UserSpacePath(ref path, ref transform)) => {
let target_transform = self.drawtarget.get_transform(); let target_transform = self.draw_target.get_transform();
let path_transform = transform.as_ref().unwrap_or(&target_transform); let path_transform = transform.as_ref().unwrap_or(&target_transform);
path.contains_point(x, y, path_transform) path.contains_point(x, y, path_transform)
}, },
@ -696,7 +696,7 @@ impl<'a> CanvasData<'a> {
fn path_builder(&mut self) -> PathBuilderRef { fn path_builder(&mut self) -> PathBuilderRef {
if self.path_state.is_none() { if self.path_state.is_none() {
self.path_state = Some(PathState::UserSpacePathBuilder( self.path_state = Some(PathState::UserSpacePathBuilder(
self.drawtarget.create_path_builder(), self.draw_target.create_path_builder(),
None, None,
)); ));
} }
@ -738,7 +738,7 @@ impl<'a> CanvasData<'a> {
PathState::DeviceSpacePathBuilder(ref mut builder) => { PathState::DeviceSpacePathBuilder(ref mut builder) => {
return PathBuilderRef { return PathBuilderRef {
builder, builder,
transform: self.drawtarget.get_transform(), transform: self.draw_target.get_transform(),
}; };
}, },
_ => unreachable!(), _ => unreachable!(),
@ -752,7 +752,7 @@ impl<'a> CanvasData<'a> {
}, },
PathState::DeviceSpacePathBuilder(ref mut builder) => PathBuilderRef { PathState::DeviceSpacePathBuilder(ref mut builder) => PathBuilderRef {
builder, builder,
transform: self.drawtarget.get_transform(), transform: self.draw_target.get_transform(),
}, },
PathState::UserSpacePathBuilder(..) | PathState::UserSpacePath(..) => unreachable!(), PathState::UserSpacePathBuilder(..) | PathState::UserSpacePath(..) => unreachable!(),
} }
@ -882,12 +882,12 @@ impl<'a> CanvasData<'a> {
pub fn set_fill_style(&mut self, style: FillOrStrokeStyle) { pub fn set_fill_style(&mut self, style: FillOrStrokeStyle) {
self.backend self.backend
.set_fill_style(style, &mut self.state, &*self.drawtarget); .set_fill_style(style, &mut self.state, &*self.draw_target);
} }
pub fn set_stroke_style(&mut self, style: FillOrStrokeStyle) { pub fn set_stroke_style(&mut self, style: FillOrStrokeStyle) {
self.backend self.backend
.set_stroke_style(style, &mut self.state, &*self.drawtarget); .set_stroke_style(style, &mut self.state, &*self.draw_target);
} }
pub fn set_line_width(&mut self, width: f32) { pub fn set_line_width(&mut self, width: f32) {
@ -907,7 +907,7 @@ impl<'a> CanvasData<'a> {
} }
pub fn get_transform(&self) -> Transform2D<f32> { pub fn get_transform(&self) -> Transform2D<f32> {
self.drawtarget.get_transform() self.draw_target.get_transform()
} }
pub fn set_transform(&mut self, transform: &Transform2D<f32>) { pub fn set_transform(&mut self, transform: &Transform2D<f32>) {
@ -918,12 +918,12 @@ impl<'a> CanvasData<'a> {
Some(PathState::UserSpacePathBuilder(_, ref mut transform)) | Some(PathState::UserSpacePathBuilder(_, ref mut transform)) |
Some(PathState::UserSpacePath(_, ref mut transform)) => { Some(PathState::UserSpacePath(_, ref mut transform)) => {
if transform.is_none() { if transform.is_none() {
*transform = Some(self.drawtarget.get_transform()); *transform = Some(self.draw_target.get_transform());
} }
}, },
} }
self.state.transform = transform.clone(); self.state.transform = transform.clone();
self.drawtarget.set_transform(transform) self.draw_target.set_transform(transform)
} }
pub fn set_global_alpha(&mut self, alpha: f32) { pub fn set_global_alpha(&mut self, alpha: f32) {
@ -935,7 +935,7 @@ impl<'a> CanvasData<'a> {
} }
pub fn recreate(&mut self, size: Size2D<u64>) { pub fn recreate(&mut self, size: Size2D<u64>) {
self.drawtarget = self self.draw_target = self
.backend .backend
.create_drawtarget(Size2D::new(size.width, size.height)); .create_drawtarget(Size2D::new(size.width, size.height));
self.state = self.backend.recreate_paint_state(&self.state); self.state = self.backend.recreate_paint_state(&self.state);
@ -954,7 +954,7 @@ impl<'a> CanvasData<'a> {
} }
pub fn send_pixels(&mut self, chan: IpcSender<IpcSharedMemory>) { pub fn send_pixels(&mut self, chan: IpcSender<IpcSharedMemory>) {
self.drawtarget.snapshot_data(&|bytes| { self.draw_target.snapshot_data(&|bytes| {
let data = IpcSharedMemory::from_bytes(bytes); let data = IpcSharedMemory::from_bytes(bytes);
chan.send(data).unwrap(); chan.send(data).unwrap();
vec![] vec![]
@ -962,7 +962,7 @@ impl<'a> CanvasData<'a> {
} }
pub fn send_data(&mut self, chan: IpcSender<CanvasImageData>) { pub fn send_data(&mut self, chan: IpcSender<CanvasImageData>) {
let size = self.drawtarget.get_size(); let size = self.draw_target.get_size();
let descriptor = webrender_api::ImageDescriptor { let descriptor = webrender_api::ImageDescriptor {
size: webrender_api::units::DeviceIntSize::new(size.width, size.height), size: webrender_api::units::DeviceIntSize::new(size.width, size.height),
@ -971,7 +971,7 @@ impl<'a> CanvasData<'a> {
offset: 0, offset: 0,
flags: webrender_api::ImageDescriptorFlags::empty(), flags: webrender_api::ImageDescriptorFlags::empty(),
}; };
let data = self.drawtarget.snapshot_data_owned(); let data = self.draw_target.snapshot_data_owned();
let data = webrender_api::ImageData::Raw(Arc::new(data)); let data = webrender_api::ImageData::Raw(Arc::new(data));
let mut updates = vec![]; let mut updates = vec![];
@ -1009,14 +1009,14 @@ impl<'a> CanvasData<'a> {
assert_eq!(rect.size.area() as usize, imagedata.len() / 4); assert_eq!(rect.size.area() as usize, imagedata.len() / 4);
pixels::rgba8_byte_swap_and_premultiply_inplace(&mut imagedata); pixels::rgba8_byte_swap_and_premultiply_inplace(&mut imagedata);
let source_surface = self let source_surface = self
.drawtarget .draw_target
.create_source_surface_from_data( .create_source_surface_from_data(
&imagedata, &imagedata,
rect.size.to_i32(), rect.size.to_i32(),
rect.size.width as i32 * 4, rect.size.width as i32 * 4,
) )
.unwrap(); .unwrap();
self.drawtarget.copy_surface( self.draw_target.copy_surface(
source_surface, source_surface,
Rect::from_size(rect.size.to_i32()), Rect::from_size(rect.size.to_i32()),
rect.origin.to_i32(), rect.origin.to_i32(),
@ -1048,12 +1048,12 @@ impl<'a> CanvasData<'a> {
} }
fn create_draw_target_for_shadow(&self, source_rect: &Rect<f32>) -> Box<dyn GenericDrawTarget> { fn create_draw_target_for_shadow(&self, source_rect: &Rect<f32>) -> Box<dyn GenericDrawTarget> {
let mut draw_target = self.drawtarget.create_similar_draw_target( let mut draw_target = self.draw_target.create_similar_draw_target(
&Size2D::new( &Size2D::new(
source_rect.size.width as i32, source_rect.size.width as i32,
source_rect.size.height as i32, source_rect.size.height as i32,
), ),
self.drawtarget.get_format(), self.draw_target.get_format(),
); );
let matrix = Transform2D::identity() let matrix = Transform2D::identity()
.pre_translate(-source_rect.origin.to_vector().cast::<f32>()) .pre_translate(-source_rect.origin.to_vector().cast::<f32>())
@ -1069,7 +1069,7 @@ impl<'a> CanvasData<'a> {
let shadow_src_rect = self.state.transform.transform_rect(rect); let shadow_src_rect = self.state.transform.transform_rect(rect);
let mut new_draw_target = self.create_draw_target_for_shadow(&shadow_src_rect); let mut new_draw_target = self.create_draw_target_for_shadow(&shadow_src_rect);
draw_shadow_source(&mut *new_draw_target); draw_shadow_source(&mut *new_draw_target);
self.drawtarget.draw_surface_with_shadow( self.draw_target.draw_surface_with_shadow(
new_draw_target.snapshot(), new_draw_target.snapshot(),
&Point2D::new( &Point2D::new(
shadow_src_rect.origin.x as f32, shadow_src_rect.origin.x as f32,
@ -1098,7 +1098,7 @@ impl<'a> CanvasData<'a> {
return vec![]; return vec![];
} }
self.drawtarget.snapshot_data(&|bytes| { self.draw_target.snapshot_data(&|bytes| {
pixels::rgba8_get_rect(bytes, canvas_size, read_rect).into_owned() pixels::rgba8_get_rect(bytes, canvas_size, read_rect).into_owned()
}) })
} }

View file

@ -988,9 +988,13 @@ impl CanvasState {
// https://html.spec.whatwg.org/multipage/#dom-context-2d-filltext // https://html.spec.whatwg.org/multipage/#dom-context-2d-filltext
pub fn fill_text(&self, text: DOMString, x: f64, y: f64, max_width: Option<f64>) { pub fn fill_text(&self, text: DOMString, x: f64, y: f64, max_width: Option<f64>) {
let parsed_text: String = text.into(); let is_max_width_finite = max_width.map_or(true, |max_width| max_width.is_finite());
if !(x.is_finite() && y.is_finite() && is_max_width_finite) {
return;
}
let style = self.state.borrow().fill_style.to_fill_or_stroke_style(); let style = self.state.borrow().fill_style.to_fill_or_stroke_style();
self.send_canvas_2d_msg(Canvas2dMsg::FillText(parsed_text, x, y, max_width, style)); self.send_canvas_2d_msg(Canvas2dMsg::FillText(text.into(), x, y, max_width, style));
} }
// https://html.spec.whatwg.org/multipage/#textmetrics // https://html.spec.whatwg.org/multipage/#textmetrics