# Copyright (c) 2010 Philip Taylor # Released under the BSD license and W3C Test Suite License: see LICENSE.txt - name: fallback.basic desc: Fallback content is inserted into the DOM testing: - fallback code: | @assert canvas.childNodes.length === 1; - name: fallback.multiple desc: Fallback content with multiple elements testing: - fallback fallback: '

FAIL

FAIL

' code: | @assert canvas.childNodes.length === 2; - name: fallback.nested desc: Fallback content containing another canvas (mostly testing parsers) testing: - fallback fallback: '

FAIL (fallback content)

FAIL (fallback content)

' code: | @assert canvas.childNodes.length === 2; - name: type.name desc: HTMLCanvasElement type and toString testing: - canvas.type code: | @assert Object.prototype.toString.call(canvas) === '[object HTMLCanvasElement]'; - name: type.exists desc: HTMLCanvasElement is a property of window notes: &bindings Defined in "Web IDL" (draft) testing: - canvas.type code: | @assert window.HTMLCanvasElement; - name: type.delete desc: window.HTMLCanvasElement interface object is [[Configurable]] notes: *bindings testing: - canvas.type code: | @assert delete window.HTMLCanvasElement === true; @assert window.HTMLCanvasElement === undefined; - name: type.prototype desc: window.HTMLCanvasElement has prototype, which is { ReadOnly, DontDelete }. prototype has getContext, which is not notes: *bindings testing: - canvas.type code: | @assert window.HTMLCanvasElement.prototype; @assert window.HTMLCanvasElement.prototype.getContext; window.HTMLCanvasElement.prototype = null; @assert window.HTMLCanvasElement.prototype; delete window.HTMLCanvasElement.prototype; @assert window.HTMLCanvasElement.prototype; window.HTMLCanvasElement.prototype.getContext = 1; @assert window.HTMLCanvasElement.prototype.getContext === 1; delete window.HTMLCanvasElement.prototype.getContext; @assert window.HTMLCanvasElement.prototype.getContext === undefined; - name: type.replace desc: HTMLCanvasElement methods can be replaced, and the replacement methods used by canvases notes: *bindings testing: - canvas.type code: | window.HTMLCanvasElement.prototype.getContext = function (name) { return 0; }; @assert canvas.getContext('2d') === 0; - name: type.extend desc: HTMLCanvasElement methods can be added, and the new methods used by canvases notes: *bindings testing: - canvas.type code: | window.HTMLCanvasElement.prototype.getZero = function () { return 0; }; @assert canvas.getZero() === 0; - name: size.attributes.idl.set.zero desc: Setting width/height IDL attributes to 0 testing: - size.width - size.height code: | canvas.width = 0; canvas.height = 0; @assert canvas.width === 0; @assert canvas.height === 0; # expected: size 0 0 # can't generate zero-sized PNG - name: size.attributes.idl desc: Getting/setting width/height IDL attributes testing: - size.width - size.height webidl: - es-unsigned-long code: | canvas.width = "100"; canvas.height = "100"; @assert canvas.width === 100; @assert canvas.height === 100; canvas.width = "+1.5e2"; canvas.height = "0x96"; @assert canvas.width === 150; @assert canvas.height === 150; canvas.width = 200 - Math.pow(2, 32); canvas.height = 200 - Math.pow(2, 32); @assert canvas.width === 200; @assert canvas.height === 200; canvas.width = 301.999; canvas.height = 301.001; @assert canvas.width === 301; @assert canvas.height === 301; canvas.width = "400x"; canvas.height = "foo"; @assert canvas.width === 0; @assert canvas.height === 0; - name: size.attributes.default desc: Default width/height when attributes are missing testing: - size.default - size.missing canvas: "" code: | @assert canvas.width === 300; @assert canvas.height === 150; @assert !canvas.hasAttribute('width'); @assert !canvas.hasAttribute('height'); expected: size 300 150 - name: size.attributes.reflect.setidl desc: Setting IDL attributes updates IDL and content attributes testing: - size.reflect code: | canvas.width = 120; canvas.height = 60; @assert canvas.getAttribute('width') === '120'; @assert canvas.getAttribute('height') === '60'; @assert canvas.width === 120; @assert canvas.height === 60; expected: size 120 60 - name: size.attributes.reflect.setidlzero desc: Setting IDL attributes to 0 updates IDL and content attributes testing: - size.reflect code: | canvas.width = 0; canvas.height = 0; @assert canvas.getAttribute('width') === '0'; @assert canvas.getAttribute('height') === '0'; @assert canvas.width === 0; @assert canvas.height === 0; # expected: size 0 0 # can't generate zero-sized PNG - name: size.attributes.reflect.setcontent desc: Setting content attributes updates IDL and content attributes testing: - size.reflect code: | canvas.setAttribute('width', '120'); canvas.setAttribute('height', '60'); @assert canvas.getAttribute('width') === '120'; @assert canvas.getAttribute('height') === '60'; @assert canvas.width === 120; @assert canvas.height === 60; expected: size 120 60 - name: size.attributes.removed desc: Removing content attributes reverts to default size testing: - size.missing canvas: width="120" height="60" code: | @assert canvas.width === 120; canvas.removeAttribute('width'); @assert canvas.width === 300; expected: size 300 60 - meta: | cases = [ ("zero", "0", 0), ("empty", "", None), ("onlyspace", " ", None), ("space", " 100", 100), ("whitespace", "\n\t\f100", 100), ("plus", "+100", 100), ("minus", "-100", None), ("octal", "0100", 100), ("hex", "0x100", 0), ("exp", "100e1", 100), ("decimal", "100.999", 100), ("percent", "100%", 100), ("em", "100em", 100), ("junk", "#!?", None), ("trailingjunk", "100#!?", 100), ] def gen(name, string, exp, code): testing = ["size.nonnegativeinteger"] if exp is None: testing.append("size.error") code += "@assert canvas.width === 300;\n@assert canvas.height === 150;\n" expected = "size 300 150" else: code += "@assert canvas.width === %s;\n@assert canvas.height === %s;\n" % (exp, exp) expected = "size %s %s" % (exp, exp) # With "100%", Opera gets canvas.width = 100 but renders at 100% of the frame width, # so check the CSS display width code += '@assert window.getComputedStyle(canvas, null).getPropertyValue("width") === "%spx";\n' % (exp, ) code += "@assert canvas.getAttribute('width') === %r;\n" % string code += "@assert canvas.getAttribute('height') === %r;\n" % string if exp == 0: expected = None # can't generate zero-sized PNGs for the expected image return code, testing, expected for name, string, exp in cases: code = "" code, testing, expected = gen(name, string, exp, code) tests.append( { "name": "size.attributes.parse.%s" % name, "desc": "Parsing of non-negative integers", "testing": testing, "canvas": 'width="%s" height="%s"' % (string, string), "code": code, "expected": expected } ) for name, string, exp in cases: code = "canvas.setAttribute('width', %r);\ncanvas.setAttribute('height', %r);\n" % (string, string) code, testing, expected = gen(name, string, exp, code) tests.append( { "name": "size.attributes.setAttribute.%s" % name, "desc": "Parsing of non-negative integers in setAttribute", "testing": testing, "canvas": 'width="50" height="50"', "code": code, "expected": expected } ) - name: size.attributes.style desc: Canvas size is independent of CSS resizing testing: - size.css canvas: 'width="50" height="30" style="width: 100px; height: 50px"' code: | @assert canvas.width === 50; @assert canvas.height === 30; expected: size 100 50 - name: size.large DISABLED: | "User agents may impose implementation-specific limits on otherwise unconstrained inputs, e.g. to prevent denial of service attacks, to guard against running out of memory, or to work around platform-specific limitations." testing: - size.width - size.height notes: Not sure how reasonable this is, but the spec doesn't say there's an upper limit on the size. code: | var n = 2147483647; // 2^31 - 1, which should be supported by any sensible definition of "long" canvas.width = n; canvas.height = n; @assert canvas.width === n; @assert canvas.height === n; # expected: size 2147483647 2147483647 # not a good idea to generate the expected image in this case... - name: initial.colour desc: Initial state is transparent black testing: - initial.colour notes: | Output should be transparent black (not transparent anything-else), but manual verification can only confirm that it's transparent - it's not possible to make the actual blackness visible. code: | @assert pixel 20,20 == 0,0,0,0; expected: size 100 50 # transparent - name: initial.reset.different desc: Changing size resets canvas to transparent black testing: - initial.reset code: | ctx.fillStyle = '#f00'; ctx.fillRect(0, 0, 50, 50); @assert pixel 20,20 == 255,0,0,255; canvas.width = 50; @assert pixel 20,20 == 0,0,0,0; expected: size 50 50 # transparent - name: initial.reset.same desc: Setting size (not changing the value) resets canvas to transparent black testing: - initial.reset code: | canvas.width = 100; ctx.fillStyle = '#f00'; ctx.fillRect(0, 0, 50, 50); @assert pixel 20,20 == 255,0,0,255; canvas.width = 100; @assert pixel 20,20 == 0,0,0,0; expected: size 100 50 # transparent - name: initial.reset.path desc: Resetting the canvas state resets the current path testing: - initial.reset code: | canvas.width = 100; ctx.rect(0, 0, 100, 50); canvas.width = 100; ctx.fillStyle = '#f00'; ctx.fill(); @assert pixel 20,20 == 0,0,0,0; expected: size 100 50 # transparent - name: initial.reset.clip desc: Resetting the canvas state resets the current clip region testing: - initial.reset code: | canvas.width = 100; ctx.rect(0, 0, 1, 1); ctx.clip(); canvas.width = 100; ctx.fillStyle = '#0f0'; ctx.fillRect(0, 0, 100, 50); @assert pixel 20,20 == 0,255,0,255; expected: green - name: initial.reset.transform desc: Resetting the canvas state resets the current transformation matrix testing: - initial.reset code: | canvas.width = 100; ctx.scale(0.1, 0.1); canvas.width = 100; ctx.fillStyle = '#0f0'; ctx.fillRect(0, 0, 100, 50); @assert pixel 20,20 == 0,255,0,255; expected: green - name: initial.reset.gradient desc: Resetting the canvas state does not invalidate any existing gradients testing: - initial.reset code: | canvas.width = 50; var g = ctx.createLinearGradient(0, 0, 100, 0); g.addColorStop(0, '#0f0'); g.addColorStop(1, '#0f0'); canvas.width = 100; ctx.fillStyle = '#f00'; ctx.fillRect(0, 0, 100, 50); ctx.fillStyle = g; ctx.fillRect(0, 0, 100, 50); @assert pixel 50,25 == 0,255,0,255; expected: green - name: initial.reset.pattern desc: Resetting the canvas state does not invalidate any existing patterns testing: - initial.reset code: | canvas.width = 30; ctx.fillStyle = '#0f0'; ctx.fillRect(0, 0, 30, 50); var p = ctx.createPattern(canvas, 'repeat-x'); canvas.width = 100; ctx.fillStyle = '#f00'; ctx.fillRect(0, 0, 100, 50); ctx.fillStyle = p; ctx.fillRect(0, 0, 100, 50); @assert pixel 50,25 == 0,255,0,255; expected: green # See tests2d.yaml for initial.reset.2dstate - name: context.emptystring desc: getContext with empty string returns null testing: - context.unrecognised code: | @assert canvas.getContext("") === null; - name: context.unrecognised.badname desc: getContext with unrecognised context name returns null testing: - context.unrecognised code: | @assert canvas.getContext('This is not an implemented context in any real browser') === null; - name: context.unrecognised.badsuffix desc: Context name "2d" plus a suffix is unrecognised testing: - context.unrecognised code: | @assert canvas.getContext("2d#") === null; - name: context.unrecognised.nullsuffix desc: Context name "2d" plus a "\0" suffix is unrecognised testing: - context.unrecognised code: | @assert canvas.getContext("2d\0") === null; - name: context.unrecognised.unicode desc: Context name which kind of looks like "2d" is unrecognised testing: - context.unrecognised code: | @assert canvas.getContext("2\uFF44") === null; // Fullwidth Latin Small Letter D - name: context.casesensitive desc: Context name "2D" is unrecognised; matching is case sensitive testing: - context.unrecognised code: | @assert canvas.getContext('2D') === null; - name: context.arguments.missing notes: *bindings testing: - canvas.getContext code: | @assert throws TypeError canvas.getContext(); @moz-todo - name: toBlob.png desc: toBlob with image/png returns a PNG Blob testing: - toBlob.png code: | canvas.toBlob(function(data){ @assert data.type === "image/png"; }, 'image/png'); - name: toBlob.jpeg desc: toBlob with image/jpeg returns a JPEG Blob testing: - toBlob.jpeg code: | canvas.toBlob(function(data){ @assert data.type === "image/jpeg"; }, 'image/jpeg'); - name: toDataURL.default desc: toDataURL with no arguments returns a PNG testing: - toDataURL.noarguments code: | var data = canvas.toDataURL(); @assert data =~ /^data:image\/png[;,]/; - name: toDataURL.png desc: toDataURL with image/png returns a PNG testing: - toDataURL.png - toDataURL.witharguments code: | var data = canvas.toDataURL('image/png'); @assert data =~ /^data:image\/png[;,]/; - name: toDataURL.jpg desc: toDataURL with image/jpg is invalid type hence returns a PNG testing: - toDataURL.jpg - toDataURL.witharguments code: | var data = canvas.toDataURL('image/jpg'); @assert data =~ /^data:image\/png[;,]/; - name: toDataURL.bogustype desc: toDataURL with a syntactically invalid type returns a PNG testing: - toDataURL.unrecognised code: | var data = canvas.toDataURL('bogus'); @assert data =~ /^data:image\/png[;,]/; - name: toDataURL.unrecognised desc: toDataURL with an unhandled type returns a PNG testing: - toDataURL.unrecognised code: | var data = canvas.toDataURL('image/example'); @assert data =~ /^data:image\/png[;,]/; - name: toDataURL.lowercase.ascii desc: toDataURL type is case-insensitive testing: - toDataURL.lowercase code: | var data = canvas.toDataURL('ImAgE/PnG'); @assert data =~ /^data:image\/png[;,]/; // If JPEG is supported at all, it must be supported case-insensitively data = canvas.toDataURL('image/jpeg'); if (data.match(/^data:image\/jpeg[;,]/)) { data = canvas.toDataURL('ImAgE/JpEg'); @assert data =~ /^data:image\/jpeg[;,]/; } - name: toDataURL.lowercase.unicode desc: toDataURL type is ASCII-case-insensitive testing: - toDataURL.lowercase code: | // Use LATIN CAPITAL LETTER I WITH DOT ABOVE (Unicode lowercase is "i") var data = canvas.toDataURL('\u0130mage/png'); @assert data =~ /^data:image\/png[;,]/; var data = canvas.toDataURL('\u0130mage/jpeg'); @assert data =~ /^data:image\/png[;,]/; - name: toDataURL.arguments.1 desc: toDataURL ignores extra arguments testing: - toDataURL.arguments code: | var data = canvas.toDataURL('image/png', 'another argument that should not raise an exception'); @assert data =~ /^data:image\/png[;,]/; - name: toDataURL.arguments.2 desc: toDataURL ignores extra arguments testing: - toDataURL.arguments code: | var data = canvas.toDataURL('image/png', 'another argument that should not raise an exception', 'and another'); @assert data =~ /^data:image\/png[;,]/; - name: toDataURL.arguments.3 desc: toDataURL ignores extra arguments testing: - toDataURL.arguments code: | // More arguments that should not raise exceptions var data = canvas.toDataURL('image/png', null, null, null); @assert data =~ /^data:image\/png[;,]/; - name: toDataURL.nocontext desc: toDataURL works before any context has been got testing: - toDataURL.noarguments code: | var canvas2 = document.createElement('canvas'); var data = canvas2.toDataURL(); @assert data =~ /^data:image\/png[;,]/; - name: toDataURL.zerosize desc: toDataURL on zero-size canvas returns 'data:,' testing: - toDataURL.zerosize canvas: width="0" height="0" code: | var data = canvas.toDataURL(); @assert data === 'data:,'; - name: toDataURL.zerowidth desc: toDataURL on zero-size canvas returns 'data:,' testing: - toDataURL.zerosize canvas: width="0" code: | var data = canvas.toDataURL(); @assert data === 'data:,'; - name: toDataURL.zeroheight desc: toDataURL on zero-size canvas returns 'data:,' testing: - toDataURL.zerosize canvas: height="0" code: | var data = canvas.toDataURL(); @assert data === 'data:,'; - name: toDataURL.large1 DISABLED: just testing implementation limits, and tends to crash canvas: width="30000" height="1" code: | var data = canvas.toDataURL(); @assert data =~ /^data:image\/png[;,]/; - name: toDataURL.large2 DISABLED: just testing implementation limits, and tends to crash canvas: width="32767" height="1" code: | var data = canvas.toDataURL(); @assert data =~ /^data:image\/png[;,]/; - name: toDataURL.large3 DISABLED: just testing implementation limits, and tends to crash canvas: width="32768" height="1" code: | var data = canvas.toDataURL(); @assert data =~ /^data:image\/png[;,]/; - name: toDataURL.png.primarycolours desc: toDataURL with PNG handles simple colours correctly testing: - toDataURL.png code: | ctx.fillStyle = '#ff0'; ctx.fillRect(0, 0, 25, 40); ctx.fillStyle = '#0ff'; ctx.fillRect(25, 0, 50, 40); ctx.fillStyle = '#00f'; ctx.fillRect(75, 0, 25, 40); ctx.fillStyle = '#fff'; ctx.fillRect(0, 40, 100, 10); var data = canvas.toDataURL(); ctx.fillStyle = '#f00'; ctx.fillRect(0, 0, 100, 50); var img = new Image(); deferTest(); img.onload = t.step_func_done(function () { ctx.drawImage(img, 0, 0); @assert pixel 12,20 == 255,255,0,255; @assert pixel 50,20 == 0,255,255,255; @assert pixel 87,20 == 0,0,255,255; @assert pixel 50,45 == 255,255,255,255; }); img.src = data; expected: | size 100 50 cr.set_source_rgb(1, 1, 0) cr.rectangle(0, 0, 25, 40) cr.fill() cr.set_source_rgb(0, 1, 1) cr.rectangle(25, 0, 50, 40) cr.fill() cr.set_source_rgb(0, 0, 1) cr.rectangle(75, 0, 25, 40) cr.fill() cr.set_source_rgb(1, 1, 1) cr.rectangle(0, 40, 100, 10) cr.fill() - name: toDataURL.png.complexcolours desc: toDataURL with PNG handles non-primary and non-solid colours correctly testing: - toDataURL.png code: | // (These values are chosen to survive relatively alright through being premultiplied) ctx.fillStyle = 'rgba(1, 3, 254, 1)'; ctx.fillRect(0, 0, 25, 25); ctx.fillStyle = 'rgba(8, 252, 248, 0.75)'; ctx.fillRect(25, 0, 25, 25); ctx.fillStyle = 'rgba(6, 10, 250, 0.502)'; ctx.fillRect(50, 0, 25, 25); ctx.fillStyle = 'rgba(12, 16, 244, 0.25)'; ctx.fillRect(75, 0, 25, 25); var img = new Image(); deferTest(); img.onload = t.step_func_done(function () { ctx.drawImage(img, 0, 25); // (The alpha values do not really survive float->int conversion, so just // do approximate comparisons) @assert pixel 12,40 == 1,3,254,255; @assert pixel 37,40 ==~ 8,252,248,191 +/- 2; @assert pixel 62,40 ==~ 6,10,250,127 +/- 4; @assert pixel 87,40 ==~ 12,16,244,63 +/- 8; }); img.src = canvas.toDataURL(); expected: | size 100 50 cr.set_source_rgba(1/255., 3/255., 254/255., 1) cr.rectangle(0, 0, 25, 50) cr.fill() cr.set_source_rgba(8/255., 252/255., 248/255., 191/255.) cr.rectangle(25, 0, 25, 50) cr.fill() cr.set_source_rgba(6/255., 10/255., 250/255., 127/255.) cr.rectangle(50, 0, 25, 50) cr.fill() cr.set_source_rgba(12/255., 16/255., 244/255., 63/255.) cr.rectangle(75, 0, 25, 50) cr.fill() - name: toDataURL.jpeg.primarycolours desc: toDataURL with JPEG handles simple colours correctly testing: - toDataURL.jpeg code: | ctx.fillStyle = '#ff0'; ctx.fillRect(0, 0, 25, 40); ctx.fillStyle = '#0ff'; ctx.fillRect(25, 0, 50, 40); ctx.fillStyle = '#00f'; ctx.fillRect(75, 0, 25, 40); ctx.fillStyle = '#fff'; ctx.fillRect(0, 40, 100, 10); var data = canvas.toDataURL('image/jpeg'); // it is okay if this returns a PNG instead ctx.fillStyle = '#f00'; ctx.fillRect(0, 0, 100, 50); var img = new Image(); deferTest(); img.onload = t.step_func_done(function () { ctx.drawImage(img, 0, 0); @assert pixel 12,20 ==~ 255,255,0,255 +/- 8; @assert pixel 50,20 ==~ 0,255,255,255 +/- 8; @assert pixel 87,20 ==~ 0,0,255,255 +/- 8; @assert pixel 50,45 ==~ 255,255,255,255 +/- 8; }); img.src = data; expected: | size 100 50 cr.set_source_rgb(1, 1, 0) cr.rectangle(0, 0, 25, 40) cr.fill() cr.set_source_rgb(0, 1, 1) cr.rectangle(25, 0, 50, 40) cr.fill() cr.set_source_rgb(0, 0, 1) cr.rectangle(75, 0, 25, 40) cr.fill() cr.set_source_rgb(1, 1, 1) cr.rectangle(0, 40, 100, 10) cr.fill() - name: toDataURL.jpeg.alpha desc: toDataURL with JPEG composites onto black testing: - toDataURL.jpeg - toDataURL.noalpha code: | ctx.fillStyle = 'rgba(128, 255, 128, 0.5)'; ctx.fillRect(0, 0, 100, 50); ctx.globalCompositeOperation = 'destination-over'; // should be ignored by toDataURL var data = canvas.toDataURL('image/jpeg'); ctx.globalCompositeOperation = 'source-over'; if (!data.match(/^data:image\/jpeg[;,]/)) { @assert true; } else { ctx.fillStyle = '#f00'; ctx.fillRect(0, 0, 100, 50); var img = new Image(); deferTest(); img.onload = t.step_func_done(function () { ctx.drawImage(img, 0, 0); @assert pixel 50,25 ==~ 63,127,63,255 +/- 8; }); img.src = data; } expected: | size 100 50 cr.set_source_rgb(0.25, 0.5, 0.25) cr.rectangle(0, 0, 100, 50) cr.fill() - name: toDataURL.jpeg.quality.basic desc: toDataURL with JPEG uses the quality parameter testing: - toDataURL.jpeg.quality mozilla: { throws } code: | ctx.fillStyle = '#00f'; ctx.fillRect(0, 0, 100, 50); ctx.fillStyle = '#0ff'; ctx.fillRect(0, 3, 100, 1); // Check for JPEG support first var data = canvas.toDataURL('image/jpeg'); if (!data.match(/^data:image\/jpeg[;,]/)) { @assert true; } else { var data_hi = canvas.toDataURL('image/jpeg', 0.99); var data_lo = canvas.toDataURL('image/jpeg', 0.01); ctx.fillStyle = '#f00'; ctx.fillRect(0, 0, 100, 50); deferTest(); var img_hi = new Image(); img_hi.onload = function () { var img_lo = new Image(); img_lo.onload = t.step_func_done(function () { ctx.drawImage(img_hi, 0, 0, 50, 50, 0, 0, 50, 50); ctx.drawImage(img_lo, 0, 0, 50, 50, 50, 0, 50, 50); @assert data_hi.length > data_lo.length; @assert pixel 25,25 ==~ 0,0,255,255 +/- 8; @assert pixel 75,25 ==~ 0,0,255,255 +/- 32; }); img_lo.src = data_lo; }; img_hi.src = data_hi; } expected: | size 100 50 cr.set_source_rgb(0, 0, 1) cr.rectangle(0, 0, 100, 50) cr.fill() cr.set_source_rgb(0, 1, 1) cr.rectangle(0, 3, 100, 1) cr.fill() - name: toDataURL.jpeg.quality.notnumber desc: toDataURL with JPEG handles non-numeric quality parameters testing: - toDataURL.jpeg.nan code: | ctx.fillStyle = '#00f'; ctx.fillRect(0, 0, 100, 50); ctx.fillStyle = '#0ff'; ctx.fillRect(0, 3, 100, 1); // Check for JPEG support first var data = canvas.toDataURL('image/jpeg'); if (!data.match(/^data:image\/jpeg[;,]/)) { @assert true; } else { @assert canvas.toDataURL('image/jpeg', 'bogus') === data; @assert canvas.toDataURL('image/jpeg', {}) === data; @assert canvas.toDataURL('image/jpeg', null) === data; @assert canvas.toDataURL('image/jpeg', undefined) === data; @assert canvas.toDataURL('image/jpeg', true) === data; @assert canvas.toDataURL('image/jpeg', '0.01') === data; } - name: toDataURL.jpeg.quality.outsiderange desc: toDataURL with JPEG handles out-of-range quality parameters testing: - toDataURL.jpeg.range code: | ctx.fillStyle = '#00f'; ctx.fillRect(0, 0, 100, 50); ctx.fillStyle = '#0ff'; ctx.fillRect(0, 3, 100, 1); // Check for JPEG support first var data = canvas.toDataURL('image/jpeg'); if (!data.match(/^data:image\/jpeg[;,]/)) { @assert true; } else { @assert canvas.toDataURL('image/jpeg', 10) === data; @assert canvas.toDataURL('image/jpeg', -10) === data; @assert canvas.toDataURL('image/jpeg', 1.01) === data; @assert canvas.toDataURL('image/jpeg', -0.01) === data; @assert canvas.toDataURL('image/jpeg', 1).length >= canvas.toDataURL('image/jpeg', 0.9).length; @assert canvas.toDataURL('image/jpeg', 0).length <= canvas.toDataURL('image/jpeg', 0.1).length; } # TODO: work out what security exception should be thrown # TODO: test same-origin vs same-host - name: security.drawImage.image desc: drawImage of different-origin image makes the canvas origin-unclean mozilla: { disabled } # relies on external resources testing: - security.drawImage.image - security.toDataURL - security.getImageData scripts: - /common/get-host-info.sub.js - data:text/javascript,addCrossOriginYellowImage() code: | ctx.drawImage(document.getElementById('yellow.png'), 0, 0); @assert throws SECURITY_ERR canvas.toDataURL(); @assert throws SECURITY_ERR ctx.getImageData(0, 0, 1, 1); - name: security.drawImage.canvas desc: drawImage of unclean canvas makes the canvas origin-unclean mozilla: { disabled } # relies on external resources testing: - security.drawImage.canvas scripts: - /common/get-host-info.sub.js - data:text/javascript,addCrossOriginYellowImage() code: | var canvas2 = document.createElement('canvas'); canvas2.width = 100; canvas2.height = 50; var ctx2 = canvas2.getContext('2d'); ctx2.drawImage(document.getElementById('yellow.png'), 0, 0); ctx.drawImage(canvas2, 0, 0); @assert throws SECURITY_ERR canvas.toDataURL(); @assert throws SECURITY_ERR ctx.getImageData(0, 0, 1, 1); - name: security.pattern.create desc: Creating an unclean pattern does not make the canvas origin-unclean mozilla: { disabled } # relies on external resources testing: - security.start scripts: - /common/get-host-info.sub.js - data:text/javascript,addCrossOriginYellowImage() code: | var p = ctx.createPattern(document.getElementById('yellow.png'), 'repeat'); canvas.toDataURL(); ctx.getImageData(0, 0, 1, 1); @assert true; // okay if there was no exception - name: security.pattern.cross desc: Using an unclean pattern makes the target canvas origin-unclean, not the pattern canvas mozilla: { disabled } # relies on external resources testing: - security.start scripts: - /common/get-host-info.sub.js - data:text/javascript,addCrossOriginYellowImage() code: | var canvas2 = document.createElement('canvas'); canvas2.width = 100; canvas2.height = 50; var ctx2 = canvas2.getContext('2d'); var p = ctx2.createPattern(document.getElementById('yellow.png'), 'repeat'); ctx.fillStyle = p; ctx.fillRect(0, 0, 100, 50); @assert throws SECURITY_ERR canvas.toDataURL(); @assert throws SECURITY_ERR ctx.getImageData(0, 0, 1, 1); canvas2.toDataURL(); ctx2.getImageData(0, 0, 1, 1); - name: security.pattern.canvas.timing desc: Pattern safety depends on whether the source was origin-clean, not on whether it still is clean notes: Disagrees with spec on "is" vs "was" mozilla: { disabled } # relies on external resources testing: - security.start - security.fillStyle.canvas scripts: - /common/get-host-info.sub.js - data:text/javascript,addCrossOriginYellowImage() code: | var canvas2 = document.createElement('canvas'); canvas2.width = 100; canvas2.height = 50; var ctx2 = canvas2.getContext('2d'); ctx2.fillStyle = '#0f0'; ctx2.fillRect(0, 0, 100, 50); var p = ctx.createPattern(canvas2, 'repeat'); ctx2.drawImage(document.getElementById('yellow.png'), 0, 0); // make canvas2 origin-unclean ctx.fillStyle = p; ctx.fillRect(0, 0, 100, 50); canvas.toDataURL(); ctx.getImageData(0, 0, 1, 1); @assert true; // okay if there was no exception - name: security.pattern.image.fillStyle desc: Setting fillStyle to a pattern of a different-origin image makes the canvas origin-unclean mozilla: { disabled } # relies on external resources testing: - security.fillStyle.image scripts: - /common/get-host-info.sub.js - data:text/javascript,addCrossOriginYellowImage() code: | var p = ctx.createPattern(document.getElementById('yellow.png'), 'repeat'); ctx.fillStyle = p; ctx.fillStyle = 'red'; @assert throws SECURITY_ERR canvas.toDataURL(); @assert throws SECURITY_ERR ctx.getImageData(0, 0, 1, 1); - name: security.pattern.canvas.fillStyle desc: Setting fillStyle to a pattern of an unclean canvas makes the canvas origin-unclean mozilla: { bug: 354127, disabled } # relies on external resources testing: - security.fillStyle.canvas scripts: - /common/get-host-info.sub.js - data:text/javascript,addCrossOriginYellowImage() code: | var canvas2 = document.createElement('canvas'); canvas2.width = 100; canvas2.height = 50; var ctx2 = canvas2.getContext('2d'); ctx2.drawImage(document.getElementById('yellow.png'), 0, 0); var p = ctx.createPattern(canvas2, 'repeat'); ctx.fillStyle = p; ctx.fillStyle = 'red'; @assert throws SECURITY_ERR canvas.toDataURL(); @assert throws SECURITY_ERR ctx.getImageData(0, 0, 1, 1); - name: security.pattern.image.strokeStyle desc: Setting strokeStyle to a pattern of a different-origin image makes the canvas origin-unclean mozilla: { disabled } # relies on external resources testing: - security.strokeStyle.image scripts: - /common/get-host-info.sub.js - data:text/javascript,addCrossOriginYellowImage() code: | var p = ctx.createPattern(document.getElementById('yellow.png'), 'repeat'); ctx.strokeStyle = p; ctx.strokeStyle = 'red'; @assert throws SECURITY_ERR canvas.toDataURL(); @assert throws SECURITY_ERR ctx.getImageData(0, 0, 1, 1); - name: security.pattern.canvas.strokeStyle desc: Setting strokeStyle to a pattern of an unclean canvas makes the canvas origin-unclean mozilla: { bug: 354127, disabled } # relies on external resources testing: - security.strokeStyle.canvas scripts: - /common/get-host-info.sub.js - data:text/javascript,addCrossOriginYellowImage() code: | var canvas2 = document.createElement('canvas'); canvas2.width = 100; canvas2.height = 50; var ctx2 = canvas2.getContext('2d'); ctx2.drawImage(document.getElementById('yellow.png'), 0, 0); var p = ctx.createPattern(canvas2, 'repeat'); ctx.strokeStyle = p; ctx.strokeStyle = 'red'; @assert throws SECURITY_ERR canvas.toDataURL(); @assert throws SECURITY_ERR ctx.getImageData(0, 0, 1, 1); - name: security.dataURI desc: 'data: URIs do not count as different-origin, and do not taint the canvas' mozilla: { disabled, bug: 417836 } # can't do "todo" so just disable it code: | ctx.fillStyle = '#0f0'; ctx.fillRect(0, 0, 100, 50); var data = canvas.toDataURL(); ctx.fillStyle = '#f00'; ctx.fillRect(0, 0, 100, 50); var img = new Image(); deferTest(); img.onload = t.step_func_done(function () { ctx.drawImage(img, 0, 0); canvas.toDataURL(); // should be permitted @assert pixel 50,25 == 0,255,0,255; }); img.src = data; expected: green - name: security.reset desc: Resetting the canvas state does not reset the origin-clean flag mozilla: { disabled } # relies on external resources testing: - initial.reset scripts: - /common/get-host-info.sub.js - data:text/javascript,addCrossOriginYellowImage() code: | canvas.width = 50; ctx.drawImage(document.getElementById('yellow.png'), 0, 0); @assert throws SECURITY_ERR canvas.toDataURL(); canvas.width = 100; @assert throws SECURITY_ERR canvas.toDataURL();