为什么使用HTMLObjectElement动态生成SVG会导致跨源错误? [英] Why does dynamically generating an SVG using HTMLObjectElement lead to a Cross-Origin error?

查看:142
本文介绍了为什么使用HTMLObjectElement动态生成SVG会导致跨源错误?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请考虑以下JavaScript代码段:

Consider the following JavaScript snippet:

const app = document.getElementById('root');
const svg = `<svg version="1.1" id="Layer_1"...`;
const obj = document.createElement('object');

obj.setAttribute('type', 'image/svg+xml');
obj.setAttribute('data', `data:image/svg+xml; base64,${btoa(svg)}`);

app.appendChild(obj);

setTimeout(() => {
  console.log(obj.contentDocument.querySelector('svg'));
}, 1500);

(参见这个JSFiddle 的完整示例)

(See this JSFiddle for a full example)

运行时,控制台中会出现以下错误(Google Chrome):

When this runs, the following error is given in the console (Google Chrome):


未捕获的DOMException:无法从'HTMLObjectElement'读取'contentDocument'属性:阻止了一个原点 https://fiddle.jshell.net 访问跨源框架。 setTimeout
https://fiddle.jshell.net/_display:77:19

Uncaught DOMException: Failed to read the 'contentDocument' property from 'HTMLObjectElement': Blocked a frame with origin "https://fiddle.jshell.net" from accessing a cross-origin frame. at setTimeout (https://fiddle.jshell.net/_display:77:19)

考虑到这一点;


  1. 为什么在尝试访问 contentDocument 完全动态创建的对象,没有外部资源?

  1. Why is this considered a cross-origin request when trying to access the contentDocument of the object that has been created entirely dynamically, with no external resources?

有没有办法以这种方式动态生成SVG,而不会冒犯浏览器跨域策略?

Is there a way to generate SVGs dynamically in this way, without offending the browsers cross-origin policy?


推荐答案

这里的问题是< a href =https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs\"rel =noreferrer> 数据:网址被视为具有唯一的来源,不同于创建嵌入式数据的上下文的来源:上下文:

The problem here is that data: URLs are treated as having a unique origin that differs from the origin of the context that created the embedded data: context:


注意:现代浏览器将数据网址视为唯一不透明的来源,而不是继承负责导航的设置对象的来源。

Note: Data URLs are treated as unique opaque origins by modern browsers, rather than inheriting the origin of the settings object responsible for the navigation.

WHATWG规范描述如何访问内容文档,其中包括交叉来源校验。 WHATWG同源比较永远不会对待传统scheme-host-porttupleorigin等于opaque data: origin。

The WHATWG specification describes how content documents are accessed, which includes a cross origin check. The WHATWG same-origin comparison will never treat a traditional scheme-host-port "tuple" origin as equal to an "opaque" data: origin.

相反,使用 Blob URL.createObjectURL 生成一个同源的临时URL,其内容可由外部环境读取:

Instead, use Blob with URL.createObjectURL to generate a same-origin temporary URL whose contents will be readable by the outer environment:

var svgUrl = URL.createObjectURL(new Blob([svg], {'type':'image/svg+xml'}));
obj.setAttribute('data', svgUrl);

我不知道为什么在原始数据: URL不是,但似乎确实有效。 (我猜是因为生成的URL只能由生成它的原点读取,而 data: URL不知道如何仅通过其原始原始文件来读取上下文。)

I don't know the security reason why this approach is allowed while a raw data: URL is not, but it does appear to work. (I guess because the generated URL is readable only by the origin that generated it, whereas a data: URL doesn't know how to be readable only by the original of its originating context.)

另请注意,某些版本的Internet Explorer支持 createObjectURL 但是错误地将生成的URL视为具有null origin,这将导致此方法失败。

Note also that some versions of Internet Explorer support createObjectURL but erroneously treat the generated URLs as having a null origin, which would cause this approach to fail.

其他选项包括:


  1. 不要使用数据: URL,而是提供与创建<的页面相同来源的SVG内容。对象> 元素。

  1. Don't use a data: URL and instead serve the SVG content from the same origin as your page that creates the <object> element.

抛弃< object> contentDocument 完全使用内联< svg> 元素小提琴):

Ditch the <object> and contentDocument altogether and use an inline <svg> element instead (fiddle):

const obj = document.createElement('div');
obj.innerHTML = svg;
app.appendChild(obj);
setTimeout(() => {
  console.log(obj.querySelector('svg'));
}, 1500);

大多数浏览器都支持内联< svg> 元素(特别是IE 9.0+;其他浏览器更早)。这意味着你可以

Most browsers support inline <svg> elements (notably, IE 9.0+; other browsers much earlier). This means you can do

<div>
    <svg>
        ...
    </svg>
</div>

它只会在< div> <中呈现SVG文档/ code>正如您所期望的那样。

and it will just render the SVG document inside the <div> as you would expect.

根据您对SVG的处理方式,您可以将其加载到 DOMParser 并执行DOM解析器中的探索/操作。

Depending on what you want to do with the SVG, you can load it into a DOMParser and do DOM exploration/manipulation within the parser.

var oParser = new DOMParser();
var svgDOM = oParser.parseFromString(svg, "text/xml");
console.log(svgDOM.documentElement.querySelector('path'));
svgDOM.documentElement.querySelector('path').remove();

但是DOM模型将与中呈现的SVG分开。对象> 。要更改< object> ,您需要序列化已解析的DOM结构并将其重新推送到数据 property:

But the DOM model will be separate from the SVG rendered in the <object>. To change the <object>, you need to serialize the parsed DOM structure and re-push it to the the data property:

var oSerializer = new XMLSerializer();
var sXML = oSerializer.serializeToString(svgDOM);
obj.setAttribute('data', `data:image/svg+xml; base64,${btoa(sXML)}`);

这似乎不具备超级性能,因为它需要浏览器重新解析一个全新的SVG文档,但它将绕过安全限制。

This doesn't seem super performant, because it needs the browser to re-parse a brand-new SVG document, but it will get around the security restrictions.

< object> 视为一个黑洞,可以接收SVG信息进行渲染,但不会再显示任何信息。但这不是一个信息问题,因为你有信息,你刚刚输入< object> :没有什么 contentDocument 可以告诉你你还不知道。

Think of the <object> as a one-way black hole that can receive SVG information to render, but will not expose any information back. This isn't an informatic problem, though, since you have the information that you just fed into the <object>: there's nothing that contentDocument can tell you that you don't already know.

但是,如果你想通过将监听器附加到组件中来使SVG内的组件成为交互式的在主页上执行代码的SVG结构,我不认为这种方法会起作用。 < object> 与其周围页面之间的分隔与< iframe> 。

However, if you want to make components within the SVG interactive by attaching listeners to components within the SVG structure that execute code on your main page, I don't think this approach will work. The separation between an <object> and its surrounding page has the same kind of embedding relationship as an <iframe>.

这篇关于为什么使用HTMLObjectElement动态生成SVG会导致跨源错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆