mirror of
https://github.com/servo/servo.git
synced 2025-08-04 05:00:08 +01:00
Add origin-clean flag tracking for canvas
This commit is contained in:
parent
c6ae32abdd
commit
b8e9064fe6
15 changed files with 135 additions and 86 deletions
|
@ -18,12 +18,14 @@ pub struct CanvasPattern {
|
||||||
surface_size: Size2D<i32>,
|
surface_size: Size2D<i32>,
|
||||||
repeat_x: bool,
|
repeat_x: bool,
|
||||||
repeat_y: bool,
|
repeat_y: bool,
|
||||||
|
origin_clean: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CanvasPattern {
|
impl CanvasPattern {
|
||||||
fn new_inherited(surface_data: Vec<u8>,
|
fn new_inherited(surface_data: Vec<u8>,
|
||||||
surface_size: Size2D<i32>,
|
surface_size: Size2D<i32>,
|
||||||
repeat: RepetitionStyle)
|
repeat: RepetitionStyle,
|
||||||
|
origin_clean: bool)
|
||||||
-> CanvasPattern {
|
-> CanvasPattern {
|
||||||
let (x, y) = match repeat {
|
let (x, y) = match repeat {
|
||||||
RepetitionStyle::Repeat => (true, true),
|
RepetitionStyle::Repeat => (true, true),
|
||||||
|
@ -38,17 +40,23 @@ impl CanvasPattern {
|
||||||
surface_size: surface_size,
|
surface_size: surface_size,
|
||||||
repeat_x: x,
|
repeat_x: x,
|
||||||
repeat_y: y,
|
repeat_y: y,
|
||||||
|
origin_clean: origin_clean,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn new(global: GlobalRef,
|
pub fn new(global: GlobalRef,
|
||||||
surface_data: Vec<u8>,
|
surface_data: Vec<u8>,
|
||||||
surface_size: Size2D<i32>,
|
surface_size: Size2D<i32>,
|
||||||
repeat: RepetitionStyle)
|
repeat: RepetitionStyle,
|
||||||
|
origin_clean: bool)
|
||||||
-> Root<CanvasPattern> {
|
-> 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,
|
global,
|
||||||
CanvasPatternBinding::Wrap)
|
CanvasPatternBinding::Wrap)
|
||||||
}
|
}
|
||||||
|
pub fn origin_is_clean(&self) -> bool {
|
||||||
|
self.origin_clean
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ToFillOrStrokeStyle for &'a CanvasPattern {
|
impl<'a> ToFillOrStrokeStyle for &'a CanvasPattern {
|
||||||
|
|
|
@ -39,6 +39,7 @@ use net_traits::image::base::PixelFormat;
|
||||||
use net_traits::image_cache_task::ImageResponse;
|
use net_traits::image_cache_task::ImageResponse;
|
||||||
use num::{Float, ToPrimitive};
|
use num::{Float, ToPrimitive};
|
||||||
use script_traits::ScriptMsg as ConstellationMsg;
|
use script_traits::ScriptMsg as ConstellationMsg;
|
||||||
|
use std::cell::Cell;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::mpsc::channel;
|
use std::sync::mpsc::channel;
|
||||||
use std::{cmp, fmt};
|
use std::{cmp, fmt};
|
||||||
|
@ -66,6 +67,7 @@ pub struct CanvasRenderingContext2D {
|
||||||
canvas: JS<HTMLCanvasElement>,
|
canvas: JS<HTMLCanvasElement>,
|
||||||
state: DOMRefCell<CanvasContextState>,
|
state: DOMRefCell<CanvasContextState>,
|
||||||
saved_states: DOMRefCell<Vec<CanvasContextState>>,
|
saved_states: DOMRefCell<Vec<CanvasContextState>>,
|
||||||
|
origin_clean: Cell<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_root]
|
#[must_root]
|
||||||
|
@ -131,6 +133,7 @@ impl CanvasRenderingContext2D {
|
||||||
canvas: JS::from_ref(canvas),
|
canvas: JS::from_ref(canvas),
|
||||||
state: DOMRefCell::new(CanvasContextState::new()),
|
state: DOMRefCell::new(CanvasContextState::new()),
|
||||||
saved_states: DOMRefCell::new(Vec::new()),
|
saved_states: DOMRefCell::new(Vec::new()),
|
||||||
|
origin_clean: Cell::new(true),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,6 +225,29 @@ impl CanvasRenderingContext2D {
|
||||||
(source_rect, dest_rect)
|
(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
|
// drawImage coordinates explained
|
||||||
//
|
//
|
||||||
|
@ -254,19 +280,20 @@ impl CanvasRenderingContext2D {
|
||||||
dw: Option<f64>,
|
dw: Option<f64>,
|
||||||
dh: Option<f64>)
|
dh: Option<f64>)
|
||||||
-> Fallible<()> {
|
-> Fallible<()> {
|
||||||
match image {
|
let result = match image {
|
||||||
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLCanvasElement(canvas) =>
|
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLCanvasElement(ref canvas) => {
|
||||||
self.draw_html_canvas_element(canvas.r(),
|
self.draw_html_canvas_element(canvas.r(),
|
||||||
sx, sy, sw, sh,
|
sx, sy, sw, sh,
|
||||||
dx, dy, dw, dh),
|
dx, dy, dw, dh)
|
||||||
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eCanvasRenderingContext2D(image) => {
|
}
|
||||||
|
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eCanvasRenderingContext2D(ref image) => {
|
||||||
let context = image.r();
|
let context = image.r();
|
||||||
let canvas = context.Canvas();
|
let canvas = context.Canvas();
|
||||||
self.draw_html_canvas_element(canvas.r(),
|
self.draw_html_canvas_element(canvas.r(),
|
||||||
sx, sy, sw, sh,
|
sx, sy, sw, sh,
|
||||||
dx, dy, dw, dh)
|
dx, dy, dw, dh)
|
||||||
}
|
}
|
||||||
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLImageElement(image) => {
|
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLImageElement(ref image) => {
|
||||||
let image_element = image.r();
|
let image_element = image.r();
|
||||||
// https://html.spec.whatwg.org/multipage/#img-error
|
// https://html.spec.whatwg.org/multipage/#img-error
|
||||||
// If the image argument is an HTMLImageElement object that is in the broken state,
|
// If the image argument is an HTMLImageElement object that is in the broken state,
|
||||||
|
@ -290,8 +317,15 @@ impl CanvasRenderingContext2D {
|
||||||
sx, sy, sw, sh,
|
sx, sy, sw, sh,
|
||||||
dx, dy, dw, dh)
|
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,
|
fn draw_html_canvas_element(&self,
|
||||||
canvas: &HTMLCanvasElement,
|
canvas: &HTMLCanvasElement,
|
||||||
|
@ -475,6 +509,14 @@ impl CanvasRenderingContext2D {
|
||||||
pub fn get_ipc_renderer(&self) -> IpcSender<CanvasMsg> {
|
pub fn get_ipc_renderer(&self) -> IpcSender<CanvasMsg> {
|
||||||
self.ipc_renderer.clone()
|
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 {
|
pub trait LayoutCanvasRenderingContext2DHelpers {
|
||||||
|
@ -887,16 +929,13 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D {
|
||||||
fn SetStrokeStyle(&self, value: StringOrCanvasGradientOrCanvasPattern) {
|
fn SetStrokeStyle(&self, value: StringOrCanvasGradientOrCanvasPattern) {
|
||||||
match value {
|
match value {
|
||||||
StringOrCanvasGradientOrCanvasPattern::eString(string) => {
|
StringOrCanvasGradientOrCanvasPattern::eString(string) => {
|
||||||
match self.parse_color(&string) {
|
if let Ok(rgba) = self.parse_color(&string) {
|
||||||
Ok(rgba) => {
|
|
||||||
self.state.borrow_mut().stroke_style = CanvasFillOrStrokeStyle::Color(rgba);
|
self.state.borrow_mut().stroke_style = CanvasFillOrStrokeStyle::Color(rgba);
|
||||||
self.ipc_renderer
|
self.ipc_renderer
|
||||||
.send(CanvasMsg::Canvas2d(Canvas2dMsg::SetStrokeStyle(
|
.send(CanvasMsg::Canvas2d(Canvas2dMsg::SetStrokeStyle(
|
||||||
FillOrStrokeStyle::Color(rgba))))
|
FillOrStrokeStyle::Color(rgba))))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
StringOrCanvasGradientOrCanvasPattern::eCanvasGradient(gradient) => {
|
StringOrCanvasGradientOrCanvasPattern::eCanvasGradient(gradient) => {
|
||||||
self.state.borrow_mut().stroke_style =
|
self.state.borrow_mut().stroke_style =
|
||||||
|
@ -905,7 +944,14 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D {
|
||||||
Canvas2dMsg::SetStrokeStyle(gradient.to_fill_or_stroke_style()));
|
Canvas2dMsg::SetStrokeStyle(gradient.to_fill_or_stroke_style()));
|
||||||
self.ipc_renderer.send(msg).unwrap();
|
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();
|
self.ipc_renderer.send(msg).unwrap();
|
||||||
}
|
}
|
||||||
StringOrCanvasGradientOrCanvasPattern::eCanvasPattern(pattern) => {
|
StringOrCanvasGradientOrCanvasPattern::eCanvasPattern(pattern) => {
|
||||||
self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::SetFillStyle(
|
let msg = CanvasMsg::Canvas2d(
|
||||||
pattern.to_fill_or_stroke_style()))).unwrap();
|
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>,
|
sw: Finite<f64>,
|
||||||
sh: Finite<f64>)
|
sh: Finite<f64>)
|
||||||
-> Fallible<Root<ImageData>> {
|
-> Fallible<Root<ImageData>> {
|
||||||
|
|
||||||
|
if !self.origin_is_clean() {
|
||||||
|
return Err(Error::Security)
|
||||||
|
}
|
||||||
|
|
||||||
let mut sx = *sx;
|
let mut sx = *sx;
|
||||||
let mut sy = *sy;
|
let mut sy = *sy;
|
||||||
let mut sw = *sw;
|
let mut sw = *sw;
|
||||||
|
@ -1095,38 +1150,34 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D {
|
||||||
repetition: DOMString)
|
repetition: DOMString)
|
||||||
-> Fallible<Root<CanvasPattern>> {
|
-> Fallible<Root<CanvasPattern>> {
|
||||||
let (image_data, image_size) = match image {
|
let (image_data, image_size) = match image {
|
||||||
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLImageElement(image) => {
|
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLImageElement(ref image) => {
|
||||||
// https://html.spec.whatwg.org/multipage/#img-error
|
// https://html.spec.whatwg.org/multipage/#img-error
|
||||||
// If the image argument is an HTMLImageElement object that is in the broken state,
|
// If the image argument is an HTMLImageElement object that is in the broken state,
|
||||||
// then throw an InvalidStateError exception
|
// then throw an InvalidStateError exception
|
||||||
match self.fetch_image_data(&image.r()) {
|
try!(self.fetch_image_data(&image.r()).ok_or(Error::InvalidState))
|
||||||
Some((data, size)) => (data, size),
|
|
||||||
None => return Err(Error::InvalidState),
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLCanvasElement(canvas) => {
|
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLCanvasElement(ref canvas) => {
|
||||||
let _ = canvas.get_or_init_2d_context();
|
let _ = canvas.get_or_init_2d_context();
|
||||||
|
|
||||||
match canvas.fetch_all_data() {
|
try!(canvas.fetch_all_data().ok_or(Error::InvalidState))
|
||||||
Some((data, size)) => (data, size),
|
|
||||||
None => return Err(Error::InvalidState),
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eCanvasRenderingContext2D(context) => {
|
HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eCanvasRenderingContext2D(ref context) => {
|
||||||
let canvas = context.Canvas();
|
let canvas = context.Canvas();
|
||||||
let _ = canvas.get_or_init_2d_context();
|
let _ = canvas.get_or_init_2d_context();
|
||||||
|
|
||||||
match canvas.fetch_all_data() {
|
try!(canvas.fetch_all_data().ok_or(Error::InvalidState))
|
||||||
Some((data, size)) => (data, size),
|
|
||||||
None => return Err(Error::InvalidState),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Ok(rep) = RepetitionStyle::from_str(&repetition) {
|
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
|
// https://html.spec.whatwg.org/multipage/#dom-context-2d-linewidth
|
||||||
|
|
|
@ -90,6 +90,13 @@ impl HTMLCanvasElement {
|
||||||
pub fn get_size(&self) -> Size2D<i32> {
|
pub fn get_size(&self) -> Size2D<i32> {
|
||||||
Size2D::new(self.Width() as i32, self.Height() as 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 {
|
pub struct HTMLCanvasData {
|
||||||
|
@ -251,8 +258,12 @@ impl HTMLCanvasElementMethods for HTMLCanvasElement {
|
||||||
_mime_type: Option<DOMString>,
|
_mime_type: Option<DOMString>,
|
||||||
_arguments: Vec<HandleValue>) -> Fallible<DOMString> {
|
_arguments: Vec<HandleValue>) -> Fallible<DOMString> {
|
||||||
|
|
||||||
// Step 1: Check the origin-clean flag (should be set in fillText/strokeText
|
if let Some(CanvasContext::Context2d(ref context)) = *self.context.borrow() {
|
||||||
// and currently unimplemented)
|
|
||||||
|
// Step 1.
|
||||||
|
if !context.origin_is_clean() {
|
||||||
|
return Err(Error::Security);
|
||||||
|
}
|
||||||
|
|
||||||
// Step 2.
|
// Step 2.
|
||||||
if self.Width() == 0 || self.Height() == 0 {
|
if self.Width() == 0 || self.Height() == 0 {
|
||||||
|
@ -260,7 +271,6 @@ impl HTMLCanvasElementMethods for HTMLCanvasElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 3.
|
// Step 3.
|
||||||
if let Some(CanvasContext::Context2d(ref context)) = *self.context.borrow() {
|
|
||||||
let window = window_from_node(self);
|
let window = window_from_node(self);
|
||||||
let image_data = try!(context.GetImageData(Finite::wrap(0f64), Finite::wrap(0f64),
|
let image_data = try!(context.GetImageData(Finite::wrap(0f64), Finite::wrap(0f64),
|
||||||
Finite::wrap(self.Width() as f64),
|
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
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
[security.drawImage.canvas.sub.html]
|
|
||||||
type: testharness
|
|
||||||
[drawImage of unclean canvas makes the canvas origin-unclean]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
[security.drawImage.image.sub.html]
|
|
||||||
type: testharness
|
|
||||||
[drawImage of different-origin image makes the canvas origin-unclean]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
[security.pattern.canvas.fillStyle.sub.html]
|
|
||||||
type: testharness
|
|
||||||
[Setting fillStyle to a pattern of an unclean canvas makes the canvas origin-unclean]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
[security.pattern.canvas.strokeStyle.sub.html]
|
|
||||||
type: testharness
|
|
||||||
[Setting strokeStyle to a pattern of an unclean canvas makes the canvas origin-unclean]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
[security.pattern.cross.sub.html]
|
|
||||||
type: testharness
|
|
||||||
[Using an unclean pattern makes the target canvas origin-unclean, not the pattern canvas]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
[security.pattern.image.fillStyle.sub.html]
|
|
||||||
type: testharness
|
|
||||||
[Setting fillStyle to a pattern of a different-origin image makes the canvas origin-unclean]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
[security.pattern.image.strokeStyle.sub.html]
|
|
||||||
type: testharness
|
|
||||||
[Setting strokeStyle to a pattern of a different-origin image makes the canvas origin-unclean]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
[security.reset.sub.html]
|
|
||||||
type: testharness
|
|
||||||
[Resetting the canvas state does not reset the origin-clean flag]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue