Reputation: 74046
Basic scenario
I'm loading several images on the client side. Some of them are from another domain, some are not. Some I may be able to access using the crossOrigin
attribute, some not.
The basic requirement is to retrieve a dataURL for the images where possible.
Question
After drawing the image to the canvas element (which I need to to to get a dataURL, right?), how can I check, without a try ... catch
block, whether the canvas has been tainted? If the canvas is tainted, I can't use toDataURL()
anymore (see MDN).
var image = new Image(),
canvas = document.createElement( 'canvas' ),
context = canvas.getContext( '2d' );
image.onload = function(){
// adjust dimensions of canvas
canvas.width = image.width;
canvas.height = image.height;
// insert image
context.drawImage( image, 0, 0 );
// how to check here?
// get dataurl
dataurl = tmpCanvas.toDataURL();
// do stuff with the dataurl
};
image.src = src; // some cross origin image
Upvotes: 22
Views: 7819
Reputation:
Here is a solution that doesn't add properties to native objects:
function isTainted(ctx) {
try {
var pixel = ctx.getImageData(0, 0, 1, 1);
return false;
} catch(err) {
return (err.code === 18);
}
}
Now simply check by doing this:
if (isTainted(ctx)) alert('Sorry, canvas is tainted!');
Edit: NOW i see you wanted a solution without try-catch. Though, this is the proper way to check as there is no origin clean flag exposed to user (it's for internal use only). Adding properties to native object is not recommended.
Upvotes: 7
Reputation: 105015
Here's an indirect test for CORS tainting that doesn't use try-catch:
A Demo: http://jsfiddle.net/m1erickson/uDt2K/
It works by setting a image.tainted=true
flag before loading the image
Then in image.onload, context.getImageData
triggers/doesn't trigger a CORS violation.
If no violation occurs, then the tainted flag is set to false (image.tainted=false
).
var img=new Image();
// set a "tainted flag to the image to true (initialize as tainted)
img.tainted=true;
img.onload=function(){
// try an action that triggers CORS security
var i=ctx.getImageData(1,1,1,1);
// if we don't get here, we violated CORS and "tainted" remains true
// if we get here, CORS is happy so set the "tainted" flag to false
img.tainted=false;
};
// test with tainted image
img.src="http://pp-group.co.uk/wp/wp-content/uploads/2013/10/house-illustration-web.gif";
Since image.onload is asynchronous, your code outside image.onload will still execute even after a CORS violation.
Here's example code that:
Example Code:
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
// known CORS violator
var src1="http://pp-group.co.uk/wp/wp-content/uploads/2013/10/house-illustration-web.gif";
// known CORS compliant
var src2="https://dl.dropboxusercontent.com/u/139992952/houseIcon.png";
// callbacks depending on if the image causes tainted canvas
function tainted(img){console.log("tainted:",img.src);}
function notTainted(img){console.log("not tainted:",img.src);}
// testing
var image1=newImage(src1,tainted,notTainted);
var image2=newImage(src2,tainted,notTainted);
function newImage(src,callWhenTainted,callWhenNotTainted){
// tmpCanvas to test CORS
var tmpCanvas=document.createElement("canvas");
var tmpCtx=tmpCanvas.getContext("2d");
// tmpCanvas just tests CORS, so make it 1x1px
tmpCanvas.width=tmpCanvas.height=1;
var img=new Image();
// set the cross origin flag (and cross our fingers!)
img.crossOrigin="anonymous";
img.onload=function(){
// add a tainted property to the image
// (initialize it to true--is tainted)
img.tainted=true;
// draw the img on the temp canvas
tmpCtx.drawImage(img,0,0);
// just in case this onload stops on a CORS error...
// set a timer to call afterOnLoad shortly
setTimeout(function(){
afterOnLoad(img,callWhenTainted,callWhenNotTainted);
},1000); // you can probably use less than 1000ms
// try to violate CORS
var i=tmpCtx.getImageData(1,1,1,1);
// if we get here, CORS is OK so set tainted=false
img.tainted=false;
};
img.src=src;
return(img);
}
// called from image.onload
// at this point the img.tainted flag is correct
function afterOnLoad(img,callWhenTainted,callWhenOK){
if(img.tainted){
// it's tainted
callWhenTainted(img);
}else{
// it's OK, do dataURL stuff
callWhenOK(img);
}
}
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="canvas" width=360 height=281></canvas>
</body>
</html>
Upvotes: 1