Skip to content

Commit

Permalink
Add origin-clean flag tracking for canvas
Browse files Browse the repository at this point in the history
  • Loading branch information
dzbarsky committed Dec 17, 2015
1 parent c6ae32a commit b8e9064
Show file tree
Hide file tree
Showing 15 changed files with 134 additions and 85 deletions.
14 changes: 11 additions & 3 deletions components/script/dom/canvaspattern.rs
Expand Up @@ -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),
Expand All @@ -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 {
Expand Down
119 changes: 85 additions & 34 deletions components/script/dom/canvasrenderingcontext2d.rs
Expand Up @@ -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};
Expand Down Expand Up @@ -66,6 +67,7 @@ pub struct CanvasRenderingContext2D {
canvas: JS<HTMLCanvasElement>,
state: DOMRefCell<CanvasContextState>,
saved_states: DOMRefCell<Vec<CanvasContextState>>,
origin_clean: Cell<bool>,
}

#[must_root]
Expand Down Expand Up @@ -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),
}
}

Expand Down Expand Up @@ -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
//
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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) => {
Expand All @@ -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();
}
}
}
}

Expand Down Expand Up @@ -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();
}
}
}
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
26 changes: 18 additions & 8 deletions components/script/dom/htmlcanvaselement.rs
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
if let Some(CanvasContext::Context2d(ref context)) = *self.context.borrow() {

// Step 2.
if self.Width() == 0 || self.Height() == 0 {
return Ok(DOMString::from("data:,"));
}
// Step 1.
if !context.origin_is_clean() {
return Err(Error::Security);
}

// Step 3.
if let Some(CanvasContext::Context2d(ref context)) = *self.context.borrow() {
// 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),
Expand Down
@@ -0,0 +1,5 @@
[security.dataURI.html]
type: testharness
[data: URIs do not count as different-origin, and do not taint the canvas]
expected: FAIL

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

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

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

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

0 comments on commit b8e9064

Please sign in to comment.