Skip to content
Permalink
Browse files

Add origin-clean flag tracking for canvas

  • Loading branch information
dzbarsky committed Dec 17, 2015
1 parent c6ae32a commit b8e9064fe634a8020b59290f03986f8ea271a92f
Showing with 134 additions and 85 deletions.
  1. +11 −3 components/script/dom/canvaspattern.rs
  2. +85 −34 components/script/dom/canvasrenderingcontext2d.rs
  3. +18 −8 components/script/dom/htmlcanvaselement.rs
  4. +5 −0 tests/wpt/metadata/html/semantics/embedded-content/the-canvas-element/security.dataURI.html.ini
  5. +0 −5 ...etadata/html/semantics/embedded-content/the-canvas-element/security.drawImage.canvas.sub.html.ini
  6. +0 −5 ...metadata/html/semantics/embedded-content/the-canvas-element/security.drawImage.image.sub.html.ini
  7. +0 −5 ...html/semantics/embedded-content/the-canvas-element/security.pattern.canvas.fillStyle.sub.html.ini
  8. +0 −5 ...ml/semantics/embedded-content/the-canvas-element/security.pattern.canvas.strokeStyle.sub.html.ini
  9. +0 −5 ...t/metadata/html/semantics/embedded-content/the-canvas-element/security.pattern.cross.sub.html.ini
  10. +0 −5 .../html/semantics/embedded-content/the-canvas-element/security.pattern.image.fillStyle.sub.html.ini
  11. +0 −5 ...tml/semantics/embedded-content/the-canvas-element/security.pattern.image.strokeStyle.sub.html.ini
  12. +0 −5 tests/wpt/metadata/html/semantics/embedded-content/the-canvas-element/security.reset.sub.html.ini
  13. +5 −0 ...etadata/html/semantics/embedded-content/the-canvas-element/toDataURL.jpeg.primarycolours.html.ini
  14. +5 −0 ...metadata/html/semantics/embedded-content/the-canvas-element/toDataURL.png.complexcolours.html.ini
  15. +5 −0 ...metadata/html/semantics/embedded-content/the-canvas-element/toDataURL.png.primarycolours.html.ini
@@ -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 {
@@ -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
@@ -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)
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),
@@ -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.
You can’t perform that action at this time.