diff options
Diffstat (limited to 'components/script/canvas_state.rs')
-rw-r--r-- | components/script/canvas_state.rs | 87 |
1 files changed, 68 insertions, 19 deletions
diff --git a/components/script/canvas_state.rs b/components/script/canvas_state.rs index 408c94c124a..e9892818e92 100644 --- a/components/script/canvas_state.rs +++ b/components/script/canvas_state.rs @@ -152,6 +152,8 @@ pub(crate) struct CanvasState { canvas_id: CanvasId, #[no_trace] image_key: ImageKey, + #[no_trace] + size: Cell<Size2D<u64>>, state: DomRefCell<CanvasContextState>, origin_clean: Cell<bool>, #[ignore_malloc_size_of = "Arc"] @@ -176,6 +178,7 @@ impl CanvasState { profiled_ipc::channel(global.time_profiler_chan().clone()).unwrap(); let script_to_constellation_chan = global.script_to_constellation_chan(); debug!("Asking constellation to create new canvas thread."); + let size = adjust_canvas_size(size); script_to_constellation_chan .send(ScriptToConstellationMessage::CreateCanvasPaintThread( size, sender, @@ -194,6 +197,7 @@ impl CanvasState { CanvasState { ipc_renderer, canvas_id, + size: Cell::new(size), state: DomRefCell::new(CanvasContextState::new()), origin_clean: Cell::new(true), image_cache: global.image_cache(), @@ -221,7 +225,15 @@ impl CanvasState { self.canvas_id } + pub(crate) fn is_paintable(&self) -> bool { + !self.size.get().is_empty() + } + pub(crate) fn send_canvas_2d_msg(&self, msg: Canvas2dMsg) { + if !self.is_paintable() { + return; + } + self.ipc_renderer .send(CanvasMsg::Canvas2d(msg, self.get_canvas_id())) .unwrap() @@ -229,6 +241,10 @@ impl CanvasState { /// Updates WR image and blocks on completion pub(crate) fn update_rendering(&self) { + if !self.is_paintable() { + return; + } + let (sender, receiver) = ipc::channel().unwrap(); self.ipc_renderer .send(CanvasMsg::Canvas2d( @@ -239,16 +255,27 @@ impl CanvasState { receiver.recv().unwrap(); } - // https://html.spec.whatwg.org/multipage/#concept-canvas-set-bitmap-dimensions + /// <https://html.spec.whatwg.org/multipage/#concept-canvas-set-bitmap-dimensions> pub(crate) fn set_bitmap_dimensions(&self, size: Size2D<u64>) { self.reset_to_initial_state(); + + self.size.replace(adjust_canvas_size(size)); + self.ipc_renderer - .send(CanvasMsg::Recreate(Some(size), self.get_canvas_id())) + .send(CanvasMsg::Recreate( + Some(self.size.get()), + self.get_canvas_id(), + )) .unwrap(); } pub(crate) fn reset(&self) { self.reset_to_initial_state(); + + if !self.is_paintable() { + return; + } + self.ipc_renderer .send(CanvasMsg::Recreate(None, self.get_canvas_id())) .unwrap(); @@ -347,7 +374,6 @@ impl CanvasState { pub(crate) fn get_rect(&self, canvas_size: Size2D<u64>, rect: Rect<u64>) -> Vec<u8> { assert!(self.origin_is_clean()); - assert!(Rect::from_size(canvas_size).contains_rect(&rect)); let (sender, receiver) = ipc::channel().unwrap(); @@ -398,18 +424,22 @@ impl CanvasState { dw: Option<f64>, dh: Option<f64>, ) -> ErrorResult { + if !self.is_paintable() { + return Ok(()); + } + let result = match image { CanvasImageSource::HTMLCanvasElement(ref canvas) => { - // https://html.spec.whatwg.org/multipage/#check-the-usability-of-the-image-argument - if !canvas.is_valid() { + // <https://html.spec.whatwg.org/multipage/#check-the-usability-of-the-image-argument> + if canvas.get_size().is_empty() { return Err(Error::InvalidState); } self.draw_html_canvas_element(canvas, htmlcanvas, sx, sy, sw, sh, dx, dy, dw, dh) }, CanvasImageSource::OffscreenCanvas(ref canvas) => { - // https://html.spec.whatwg.org/multipage/#check-the-usability-of-the-image-argument - if !canvas.is_valid() { + // <https://html.spec.whatwg.org/multipage/#check-the-usability-of-the-image-argument> + if canvas.get_size().is_empty() { return Err(Error::InvalidState); } @@ -528,11 +558,6 @@ impl CanvasState { dw: Option<f64>, dh: Option<f64>, ) -> ErrorResult { - // 1. Check the usability of the image argument - if !canvas.is_valid() { - return Err(Error::InvalidState); - } - let canvas_size = canvas.get_size(); let dw = dw.unwrap_or(canvas_size.width as f64); let dh = dh.unwrap_or(canvas_size.height as f64); @@ -1403,13 +1428,13 @@ impl CanvasState { }, }; - ImageData::new( - global, - size.width, - size.height, - Some(self.get_rect(canvas_size, read_rect)), - can_gc, - ) + let data = if self.is_paintable() { + Some(self.get_rect(canvas_size, read_rect)) + } else { + None + }; + + ImageData::new(global, size.width, size.height, data, can_gc) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-putimagedata @@ -1445,6 +1470,10 @@ impl CanvasState { dirty_width: i32, dirty_height: i32, ) { + if !self.is_paintable() { + return; + } + // FIXME(nox): There are many arithmetic operations here that can // overflow or underflow, this should probably be audited. @@ -2013,3 +2042,23 @@ where style.font_family.to_css_string() ) } + +fn adjust_canvas_size(size: Size2D<u64>) -> Size2D<u64> { + // Firefox limits width/height to 32767 pixels and Chromium to 65535 pixels, + // but slows down dramatically before it reaches that limit. + // We limit by area instead, giving us larger maximum dimensions, + // in exchange for a smaller maximum canvas size. + const MAX_CANVAS_AREA: u64 = 32768 * 8192; + // Max width/height to 65535 in CSS pixels. + const MAX_CANVAS_SIZE: u64 = 65535; + + if !size.is_empty() && + size.greater_than(Size2D::new(MAX_CANVAS_SIZE, MAX_CANVAS_SIZE)) + .none() && + size.area() < MAX_CANVAS_AREA + { + size + } else { + Size2D::zero() + } +} |