niklasvh / html2canvas

Screenshots with JavaScript
https://html2canvas.hertzen.com/
MIT License
30.7k stars 4.82k forks source link

CORS #1544

Open icezeko opened 6 years ago

icezeko commented 6 years ago

i'm trying to take a shot of a google recaptcha challenge loaded by iframe from another iframe, already used allowTaint and useCORS, and all i get is a error.

0ms html2canvas: html2canvas 1.0.0-alpha.12 10ms html2canvas: DOMException: Failed to execute 'open' on 'Document': Can only call open() on same-origin documents. DOMException: Failed to execute 'open' on 'Document': Can only call open() on same-origin documents.

CrandellWS commented 6 years ago

I fixed some cors issues I was having with the combination of Google Chrome, AWS S3, and multiple origins.

I found this stackoverflow thread: https://stackoverflow.com/questions/26352083/chrome-cors-cache-requesting-same-file-from-two-different-origins

Which links to this bug report: https://bugs.chromium.org/p/chromium/issues/detail?id=260239

Anyhow as workaround solution you can try this modified version of html2canvas: https://gist.github.com/CrandellWS/6bc2078aced496004d7a045e6360f19b

use the options:

allowTaint : false,
useCORS: true

Hope that helps.

FYI, this will add the current time stamp to cors image urls to sidestep a cache issue I was having on Chrome... https://gist.github.com/CrandellWS/6bc2078aced496004d7a045e6360f19b#file-html2canvas-js-L6838

Which means it will effect performance by re-downloading those images...

pritambios commented 5 years ago

Thank you @CrandellWS I have solved it using your solution this way.

const image = "https://s3-us-west-2.amazonaws.com/github.png?1551296103"
const timestamp = new Date().getTime();
const imageWithTimestamp = image.includes('?') ? `${image}&v=${timestamp}` : `${image}?v=${timestamp}`;

render() {
  return (
    <div>
      <h1>Hello world</h1>
      <img
         src={imageWithTimestamp}
         alt={imageName}
         crossOrigin="anonymous"
      />
    </div>
  );
}

Please make sure that you image is returning CORS enable headers. Otherwise you have to add CORS policy to your s3 bucket.

Rostislav-Poleshko commented 5 years ago

Thank you @pritam1994

sougatabose07 commented 5 years ago

Hi,

I have added the following options -

allowTaint : false,
useCORS: true

Bit it is not showing captcha. Instead a blank box.

gaoming13 commented 4 years ago

Without modifying html2canvas, here is an idea: First use canvas to convert cross-domain pictures into Base64 encoding, and then give it to html2canvas for processing

// 1.First get the Base64 encoding of cross-domain pictures through canvas
const img = new Image();
img.onload = () => {
  const c = this.$refs.crossImg;
  const ctx = c.getContext('2d');
  ctx.drawImage(img, 0, 0, c.width, c.height);
  this.crossImgBase64 = c.toDataURL('image/png');

  // 2.Draw
  Html2canvas(this.$refs.html, {
    // useCORS: true,
  }).then((canvas) => {
    this.pngBase64 = canvas.toDataURL('image/png');
    this.isShowPng = true;
  });
};
img.crossOrigin = 'anonymous';
img.src = 'https://secure.gravatar.com/avatar/03af4ed2fa8526e9f37c847cc4083141';

Here a example

https://vue-html2canvas-safari-bug.stackblitz.io/

rup9823 commented 3 years ago

I am using version 1.0.0-rc.0 and just adding @CrandellWS's solution didn't work The part of html2canvas.js file where the correction has to be made is like this

if (!supportsDataImages || useCORS){
    img.crossOrigin = 'anonymous';
} 
    img.onerror = reject;
    img.src = src;

Adding src alone won't work, the code has to be like this

if (!supportsDataImages || useCORS){
    img.crossOrigin = 'anonymous';
    img.src = src +'?v='+new Date().getTime();
} 
else{
    img.src = src;
}
    img.onerror = reject;
SalahAdDin commented 2 years ago

It seems it is not fixed yet.

yulisartika commented 2 years ago

been stuck for days and finally found the solution from here: https://developpaper.com/question/img-add-crossorigin-anonymous-and-the-picture-will-not-be-displayed/

in addition, I added:

useCORS: true

and on the img tag: <img imageUrl + '?time=' + new Date().valueOf() crossorigin='anonymous'>

hope this helps

SalahAdDin commented 2 years ago

@yulisartika Well, when i put crossorigin='anonymou' the brower simply can't fetch the image, hence, it can't show them.

CrandellWS commented 2 years ago

@SalahAdDin as @yulisartika said

and on the img tag: <img imageUrl + '?time=' + new Date().valueOf() crossorigin='anonymous'>

setting the image url to have an unique parameter is key

arohablue commented 2 years ago

been stuck for days and finally found the solution from here: https://developpaper.com/question/img-add-crossorigin-anonymous-and-the-picture-will-not-be-displayed/

in addition, I added:

useCORS: true

and on the img tag: <img imageUrl + '?time=' + new Date().valueOf() crossorigin='anonymous'>

hope this helps

@yulisartika Well, when i put crossorigin='anonymou' the brower simply can't fetch the image, hence, it can't show them.

Adding crossOrigin="" did it for me.

cezarcozta commented 2 years ago

@arohablue for me did not =´(

SalahAdDin commented 2 years ago

@arohablue for me did not =´(

We had to make a proxy server.

arohablue commented 2 years ago

@arohablue for me did not =´(

For me the image is stored on WAS so I also added a cors filter there and used the below to fetch it. <img src={bucketURL+ '?time=' + new Date().valueOf()} crossOrigin=""/>

IntelligaiaVivek commented 2 years ago

As i need to get screenshot for iframe from other page but its in same domain but then also getting this error:- Failed to execute 'open' on 'Document': Can only call open() on same-origin documents Getting this error on html2canvas any help appreciated

SalahAdDin commented 2 years ago

As i need to get screenshot for iframe from other page but its in same domain but then also getting this error:- Failed to execute 'open' on 'Document': Can only call open() on same-origin documents Getting this error on html2canvas any help appreciated

Proxy server I would say.

yanghoxom commented 2 years ago

Has anyone had the same situation as me? some images can be downloaded, but some give cors error even though they are in the same bucket s3 bucket setup cors AllowOriginls: ["*"] I use the option useCORS: true

RSchneider94 commented 2 years ago

for people still having the issue, for me what worked was a mix of some replies here.. I have:

<img imageUrl + '?time=' + new Date().valueOf() crossorigin="">

plus { allowTaint: true, useCORS: true } in html2canvas and then it worked! Image is hosted on S3.

xrzhuang commented 1 year ago

@RSchneider94 i wish it wa that easy for some reason when I have crossorigin="" or anonymous my images don't even render at all.

@memsenpai I have the exact same issue as you my S3 bucket has AllowOrigin wildcard

w4cernesto commented 1 year ago

been stuck for days and finally found the solution from here: https://developpaper.com/question/img-add-crossorigin-anonymous-and-the-picture-will-not-be-displayed/ in addition, I added: useCORS: true and on the img tag: <img imageUrl + '?time=' + new Date().valueOf() crossorigin='anonymous'> hope this helps

@yulisartika Well, when i put crossorigin='anonymou' the brower simply can't fetch the image, hence, it can't show them.

Adding crossOrigin="" did it for me.

For me was the response!!

yanghoxom commented 1 year ago

@xrzhuang I must use a server as a proxy to solve that issue :(

zakpucci commented 8 months ago

For anyone who may have run into this seemingly simple issue. As long your application can make cross origin requests fine and your only issue is html2canvas not being able to reach those URLs, you can convert your visible images to data URLs prior to the canvas printing to bypass the need for html2canvas to fetch them cross origin.

This is the only way I was able to get around this without proxy or reworking CORS in my functions or server.

-- Loop over your node for img tags, set the src to await toDataURL(img.src)

const toDataURL = async (src) => {
  let blob = await fetch(src).then((r) => r.blob());
  let d = await new Promise((resolve) => {
    let reader = new FileReader();
    reader.onload = () => resolve(reader.result);
    reader.readAsDataURL(blob);
  });
  return d;
};