Add origin-clean flag tracking for canvas

This commit is contained in:
David Zbarsky 2015-11-06 00:20:18 -08:00
parent c6ae32abdd
commit b8e9064fe6
15 changed files with 135 additions and 86 deletions

View file

@ -18,12 +18,14 @@ pub struct CanvasPattern {
surface_size: Size2D<i32>,
repeat_x: bool,
repeat_y: bool,
origin_clean: bool,
}
impl CanvasPattern {
fn new_inherited(surface_data: Vec<u8>,
surface_size: Size2D<i32>,
repeat: RepetitionStyle)
repeat: RepetitionStyle,
origin_clean: bool)
-> CanvasPattern {
let (x, y) = match repeat {
RepetitionStyle::Repeat => (true, true),
@ -38,17 +40,23 @@ impl CanvasPattern {
surface_size: surface_size,
repeat_x: x,
repeat_y: y,
origin_clean: origin_clean,
}
}
pub fn new(global: GlobalRef,
surface_data: Vec<u8>,
surface_size: Size2D<i32>,
repeat: RepetitionStyle)
repeat: RepetitionStyle,
origin_clean: bool)
-> Root<CanvasPattern> {
reflect_dom_object(box CanvasPattern::new_inherited(surface_data, surface_size, repeat),
reflect_dom_object(box CanvasPattern::new_inherited(surface_data, surface_size,
repeat, origin_clean),
global,
CanvasPatternBinding::Wrap)
}
pub fn origin_is_clean(&self) -> bool {
self.origin_clean
}
}
impl<'a> ToFillOrStrokeStyle for &'a CanvasPattern {

View file

@ -39,6 +39,7 @@ use net_traits::image::base::PixelFormat;
use net_traits::image_cache_task::ImageResponse;
use num::{Float, ToPrimitive};
use script_traits::ScriptMsg as ConstellationMsg;
use std::cell::Cell;
use std::str::FromStr;
use std::sync::mpsc::channel;
use std::{cmp, fmt};
@ -66,6 +67,7 @@ pub struct CanvasRenderingContext2D {
canvas: JS<HTMLCanvasElement>,
state: DOMRefCell<CanvasContextState>,
saved_states: DOMRefCell<Vec<CanvasContextState>>,
origin_clean: Cell<bool>,
}
#[must_root]
@ -131,6 +133,7 @@ impl CanvasRenderingContext2D {
canvas: JS::from_ref(canvas),
state: DOMRefCell::new(CanvasContextState::new()),
saved_states: DOMRefCell::new(Vec::new()),
origin_clean: Cell::new(true),
}
}
@ -222,6 +225,29 @@ impl CanvasRenderingContext2D {
(source_rect, dest_rect)
}
// https://html.spec.whatwg.org/multipage/#the-image-argument-is-not-origin-clean
fn is_origin_clean(&self,
image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D)
-> bool {
match image {
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLCanvasElement(canvas) => {
canvas.origin_is_clean()
}
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eCanvasRenderingContext2D(image) =>
image.r().origin_is_clean(),
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLImageElement(image) =>
match image.get_url() {
None => true,
Some(url) => {
// TODO(zbarsky): we should check the origin of the image against
// the entry settings object, but for now check it against the canvas' doc.
let node: &Node = &*self.canvas.upcast();
url.origin() == node.owner_doc().url().origin()
}
}
}
}
//
// drawImage coordinates explained
//
@ -254,19 +280,20 @@ impl CanvasRenderingContext2D {
dw: Option<f64>,
dh: Option<f64>)
-> Fallible<()> {
match image {
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLCanvasElement(canvas) =>
let result = match image {
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLCanvasElement(ref canvas) => {
self.draw_html_canvas_element(canvas.r(),
sx, sy, sw, sh,
dx, dy, dw, dh),
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eCanvasRenderingContext2D(image) => {
dx, dy, dw, dh)
}
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eCanvasRenderingContext2D(ref image) => {
let context = image.r();
let canvas = context.Canvas();
self.draw_html_canvas_element(canvas.r(),
sx, sy, sw, sh,
dx, dy, dw, dh)
}
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLImageElement(image) => {
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLImageElement(ref image) => {
let image_element = image.r();
// https://html.spec.whatwg.org/multipage/#img-error
// If the image argument is an HTMLImageElement object that is in the broken state,
@ -290,7 +317,14 @@ impl CanvasRenderingContext2D {
sx, sy, sw, sh,
dx, dy, dw, dh)
}
};
if result.is_ok() {
if !self.is_origin_clean(image) {
self.set_origin_unclean()
}
}
result
}
fn draw_html_canvas_element(&self,
@ -475,6 +509,14 @@ impl CanvasRenderingContext2D {
pub fn get_ipc_renderer(&self) -> IpcSender<CanvasMsg> {
self.ipc_renderer.clone()
}
pub fn origin_is_clean(&self) -> bool {
self.origin_clean.get()
}
fn set_origin_unclean(&self) {
self.origin_clean.set(false)
}
}
pub trait LayoutCanvasRenderingContext2DHelpers {
@ -887,15 +929,12 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D {
fn SetStrokeStyle(&self, value: StringOrCanvasGradientOrCanvasPattern) {
match value {
StringOrCanvasGradientOrCanvasPattern::eString(string) => {
match self.parse_color(&string) {
Ok(rgba) => {
self.state.borrow_mut().stroke_style = CanvasFillOrStrokeStyle::Color(rgba);
self.ipc_renderer
.send(CanvasMsg::Canvas2d(Canvas2dMsg::SetStrokeStyle(
FillOrStrokeStyle::Color(rgba))))
.unwrap();
}
_ => {}
if let Ok(rgba) = self.parse_color(&string) {
self.state.borrow_mut().stroke_style = CanvasFillOrStrokeStyle::Color(rgba);
self.ipc_renderer
.send(CanvasMsg::Canvas2d(Canvas2dMsg::SetStrokeStyle(
FillOrStrokeStyle::Color(rgba))))
.unwrap();
}
},
StringOrCanvasGradientOrCanvasPattern::eCanvasGradient(gradient) => {
@ -905,7 +944,14 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D {
Canvas2dMsg::SetStrokeStyle(gradient.to_fill_or_stroke_style()));
self.ipc_renderer.send(msg).unwrap();
},
_ => {}
StringOrCanvasGradientOrCanvasPattern::eCanvasPattern(pattern) => {
let msg = CanvasMsg::Canvas2d(
Canvas2dMsg::SetStrokeStyle(pattern.to_fill_or_stroke_style()));
self.ipc_renderer.send(msg).unwrap();
if !pattern.origin_is_clean() {
self.set_origin_unclean();
}
}
}
}
@ -943,8 +989,12 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D {
self.ipc_renderer.send(msg).unwrap();
}
StringOrCanvasGradientOrCanvasPattern::eCanvasPattern(pattern) => {
self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::SetFillStyle(
pattern.to_fill_or_stroke_style()))).unwrap();
let msg = CanvasMsg::Canvas2d(
Canvas2dMsg::SetFillStyle(pattern.to_fill_or_stroke_style()));
self.ipc_renderer.send(msg).unwrap();
if !pattern.origin_is_clean() {
self.set_origin_unclean();
}
}
}
}
@ -975,6 +1025,11 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D {
sw: Finite<f64>,
sh: Finite<f64>)
-> Fallible<Root<ImageData>> {
if !self.origin_is_clean() {
return Err(Error::Security)
}
let mut sx = *sx;
let mut sy = *sy;
let mut sw = *sw;
@ -1095,38 +1150,34 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D {
repetition: DOMString)
-> Fallible<Root<CanvasPattern>> {
let (image_data, image_size) = match image {
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLImageElement(image) => {
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLImageElement(ref image) => {
// https://html.spec.whatwg.org/multipage/#img-error
// If the image argument is an HTMLImageElement object that is in the broken state,
// then throw an InvalidStateError exception
match self.fetch_image_data(&image.r()) {
Some((data, size)) => (data, size),
None => return Err(Error::InvalidState),
}
try!(self.fetch_image_data(&image.r()).ok_or(Error::InvalidState))
},
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLCanvasElement(canvas) => {
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLCanvasElement(ref canvas) => {
let _ = canvas.get_or_init_2d_context();
match canvas.fetch_all_data() {
Some((data, size)) => (data, size),
None => return Err(Error::InvalidState),
}
try!(canvas.fetch_all_data().ok_or(Error::InvalidState))
},
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eCanvasRenderingContext2D(context) => {
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eCanvasRenderingContext2D(ref context) => {
let canvas = context.Canvas();
let _ = canvas.get_or_init_2d_context();
match canvas.fetch_all_data() {
Some((data, size)) => (data, size),
None => return Err(Error::InvalidState),
}
try!(canvas.fetch_all_data().ok_or(Error::InvalidState))
}
};
if let Ok(rep) = RepetitionStyle::from_str(&repetition) {
return Ok(CanvasPattern::new(self.global.root().r(), image_data, image_size, rep));
Ok(CanvasPattern::new(self.global.root().r(),
image_data,
image_size,
rep,
self.is_origin_clean(image)))
} else {
Err(Error::Syntax)
}
return Err(Error::Syntax);
}
// https://html.spec.whatwg.org/multipage/#dom-context-2d-linewidth

View file

@ -90,6 +90,13 @@ impl HTMLCanvasElement {
pub fn get_size(&self) -> Size2D<i32> {
Size2D::new(self.Width() as i32, self.Height() as i32)
}
pub fn origin_is_clean(&self) -> bool {
match *self.context.borrow() {
Some(CanvasContext::Context2d(ref context)) => context.origin_is_clean(),
_ => true,
}
}
}
pub struct HTMLCanvasData {
@ -251,16 +258,19 @@ impl HTMLCanvasElementMethods for HTMLCanvasElement {
_mime_type: Option<DOMString>,
_arguments: Vec<HandleValue>) -> Fallible<DOMString> {
// Step 1: Check the origin-clean flag (should be set in fillText/strokeText
// and currently unimplemented)
// Step 2.
if self.Width() == 0 || self.Height() == 0 {
return Ok(DOMString::from("data:,"));
}
// Step 3.
if let Some(CanvasContext::Context2d(ref context)) = *self.context.borrow() {
// Step 1.
if !context.origin_is_clean() {
return Err(Error::Security);
}
// Step 2.
if self.Width() == 0 || self.Height() == 0 {
return Ok(DOMString::from("data:,"));
}
// Step 3.
let window = window_from_node(self);
let image_data = try!(context.GetImageData(Finite::wrap(0f64), Finite::wrap(0f64),
Finite::wrap(self.Width() as f64),