"可能无法加载受污染的画布"WebGL纹理的跨域问题

Wil*_*ood 6 gwt google-chrome webgl libgdx html5-canvas

我在过去48小时内学到了很多关于跨域策略的知识,但显然还不够.

这个问题之后.我的HTML5游戏支持Facebook登录.我正在尝试下载人们朋友的个人资料照片.在我的游戏的HTML5版本中,我在Chrome中遇到以下错误.

detailMessage:"com.google.gwt.core.client.JavaScriptException :( SecurityError)↵stack:错误:无法在'WebGLRenderingContext'上执行'texImage2D':可能无法加载受污染的画布.

据我所知,出现此错误是因为我正在尝试从其他域加载图像,但这可以使用Access-Control-Allow-Origin标头解决,详见问题.

我试图从中下载的URL是

https://graph.facebook.com/1387819034852828/picture?width=150&height=150

查看Chrome中的网络标签,我可以看到它具有所需的access-control-allow-origin标头,并通过302重定向到新URL进行响应.该URL有所不同,我想这取决于负载平衡,但这是一个示例URL.

https://fbcdn-profile-a.akamaihd.net/hprofile-ak-xap1/v/t1.0-1/c0.0.160.160/p160x160/11046398_1413754142259317_606640341449680402_n.jpg?oh=6738b578bc134ff207679c832ecd5fe5&oe=562F72A4& GDA = 1445979187_2b0bf0ad3272047d64c7bfc2dbc09a29

此URL还具有access-control-allow-origin标头.所以我不明白为什么会失败.

作为Facebook,以及成千上万的应用程序,游戏和网站显示用户个人资料图片的事实,我认为这是可能的.我知道我可以通过我自己的服务器弹跳,但我不知道为什么我应该这样做.

回答

我最终使用以下代码在libgdx中进行跨域映像加载(这非常hacky,我确信可以改进).我还没有设法使用AssetDownloader.我希望最终可以解决这个问题.

public void downloadPixmap(final String url, final DownloadPixmapResponse response) {
    final RootPanel root = RootPanel.get("embed-html");
    final Image img = new Image(url);
    img.getElement().setAttribute("crossOrigin", "anonymous");
    img.addLoadHandler(new LoadHandler() {

        @Override
        public void onLoad(LoadEvent event) {
            HtmlLauncher.application.getPreloader().images.put(url, ImageElement.as(img.getElement()));
            response.downloadComplete(new Pixmap(Gdx.files.internal(url)));
            root.remove(img);
        }
    });
    root.add(img);
}

interface DownloadPixmapResponse {
    void downloadComplete(Pixmap pixmap);

    void downloadFailed(Throwable e);
}
Run Code Online (Sandbox Code Playgroud)

gma*_*man 18

crossOrigin在申请之前在你的img上设置属性吗?

var img = new Image();
img.crossOrigin = "anonymous";
img.src = "https://graph.facebook.com/1387819034852828/picture?width=150&height=150"; 
Run Code Online (Sandbox Code Playgroud)

当问到这个问题时,这对我有用.不幸的是,上面的URL不再指向任何内容,因此我在下面的示例中对其进行了更改

var img = new Image();
img.crossOrigin = "anonymous";   // COMMENT OUT TO SEE IT FAIL
img.onload = uploadTex;
img.src = "https://i.imgur.com/ZKMnXce.png"; 

function uploadTex() {
  var gl = document.createElement("canvas").getContext("webgl");
  var tex = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, tex);
  try {
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
    log("DONE: ", gl.getError());
  } catch (e) {
    log("FAILED to use image because of security:", e);
  }
}

function log() {
  var div = document.createElement("div");
  div.innerHTML = Array.prototype.join.call(arguments, " ");
  document.body.appendChild(div);
}
Run Code Online (Sandbox Code Playgroud)
<body></body>
Run Code Online (Sandbox Code Playgroud)

如何检查您是否收到了标题

打开您的devtools,选择网络选项卡,重新加载页面,选择有问题的图像,查看REQUEST标头和RESPONSE标头.

在此输入图像描述

该请求应显示您的浏览器发送了Origin:标题

回复应该表明你收到了

Access-Control-Allow-Methods: GET, OPTIONS, ...
Access-Control-Allow-Origin: *
Run Code Online (Sandbox Code Playgroud)

请注意,响应和请求都必须显示上面的条目.如果请求丢失,Origin:那么您没有设置img.crossOrigin,即使响应表明它没问题,浏览器也不会让您使用该图像.

如果您的请求具有Origin:标头且响应没有其他标头,则该服务器未授予使用该图像显示它的权限.换句话说,它将在图像标签中工作,你可以将它绘制到画布上,但你不能在WebGL中使用它,你绘制的任何2d画布都会被污染,toDataURL并且getImageData会停止工作