| # 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: '<p class="fallback">FAIL</p><p class="fallback">FAIL</p>' |
| code: | |
| @assert canvas.childNodes.length === 2; |
| |
| - name: fallback.nested |
| desc: Fallback content containing another canvas (mostly testing parsers) |
| testing: |
| - fallback |
| fallback: '<canvas><p class="fallback">FAIL (fallback content)</p></canvas><p class="fallback">FAIL (fallback content)</p>' |
| 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.sub |
| 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 |
| images: |
| - http://{{domains[www2]}}:{{ports[http][0]}}/images/yellow.png |
| 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.sub |
| desc: drawImage of unclean canvas makes the canvas origin-unclean |
| mozilla: { disabled } # relies on external resources |
| testing: |
| - security.drawImage.canvas |
| images: |
| - http://{{domains[www2]}}:{{ports[http][0]}}/images/yellow.png |
| 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.sub |
| desc: Creating an unclean pattern does not make the canvas origin-unclean |
| mozilla: { disabled } # relies on external resources |
| testing: |
| - security.start |
| images: |
| - http://{{domains[www]}}:{{ports[http][0]}}/images/yellow.png |
| 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.sub |
| desc: Using an unclean pattern makes the target canvas origin-unclean, not the pattern canvas |
| mozilla: { disabled } # relies on external resources |
| testing: |
| - security.start |
| images: |
| - http://{{domains[www2]}}:{{ports[http][0]}}/images/yellow.png |
| 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.sub |
| 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 |
| images: |
| - http://{{domains[www2]}}:{{ports[http][0]}}/images/yellow.png |
| 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.sub |
| 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 |
| images: |
| - http://{{domains[www2]}}:{{ports[http][0]}}/images/yellow.png |
| 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.sub |
| 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 |
| images: |
| - http://{{domains[www2]}}:{{ports[http][0]}}/images/yellow.png |
| 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.sub |
| 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 |
| images: |
| - http://{{domains[www2]}}:{{ports[http][0]}}/images/yellow.png |
| 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.sub |
| 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 |
| images: |
| - http://{{domains[www2]}}:{{ports[http][0]}}/images/yellow.png |
| 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.sub |
| desc: Resetting the canvas state does not reset the origin-clean flag |
| mozilla: { disabled } # relies on external resources |
| testing: |
| - initial.reset |
| images: |
| - http://{{domains[www2]}}:{{ports[http][0]}}/images/yellow.png |
| 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(); |