为什么使用HTMLObjectElement动态生成SVG会导致跨源错误?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (92)

考虑下面的JavaScript片段:

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);

运行时,控制台(Google Chrome)会提供以下错误信息:

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完全动态创建的对象时没有考虑外部资源,这被认为是一个跨源请求?
  2. 有没有办法以这种方式动态生成SVG,而不会违反浏览器的跨源策略?
提问于
用户回答回答于

这里的问题是,data:URL被视为具有与创建嵌入式data:上下文的上下文的来源不同的唯一来源:

注意:数据URL被现代浏览器视为独特的不透明起源,而不是继承负责导航的设置对象的来源。

WHATWG规范描述了如何访问内容文档,其中包括一个跨源检查。在WHATWG同源比较绝不会像传统方案主机端口“元组”起源等于“不透明” data:的起源。

相反,使用BlobURL.createObjectURL生成一个相同来源的临时网址,其内容将是可读的外部环境:

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

我不知道为什么在原始data:URL 不允许的情况下允许使用这种方法的安全原因,但似乎可行。(我猜是因为生成的URL只能由生成该data:URL 的原始URL读取,而URL不知道如何仅由其原始上下文的原始内容读取。)

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

其他选项是:

  1. 请勿使用data:网址,而是以与创建<object>元素的页面相同的来源提供SVG内容。
  2. <object>contentDocument完全使用内联<svg>元素: const obj = document.createElement('div'); obj.innerHTML = svg; app.appendChild(obj); setTimeout(() => { console.log(obj.querySelector('svg')); }, 1500); 大多数浏览器支持内联<svg>元素(特别是IE 9.0+;其他浏览器更早)。这意味着你可以做到 <div> <svg> ... </svg> </div> 它只会<div>像你期望的那样渲染SVG文档。
  3. 根据你想要用SVG做什么,你可以把它加载到一个DOMParser DOM中,并在解析器中进行DOM探索/操作。 var oParser = new DOMParser(); var svgDOM = oParser.parseFromString(svg, "text/xml"); console.log(svgDOM.documentElement.querySelector('path')); svgDOM.documentElement.querySelector('path').remove(); 但是DOM模型将与SVG中呈现的SVG分开<object>。要更改<object>data属性,需要序列化已解析的DOM结构并重新将其推送到该属性: var oSerializer = new XMLSerializer(); var sXML = oSerializer.serializeToString(svgDOM); obj.setAttribute('data', `data:image/svg+xml; base64,${btoa(sXML)}`); 这看起来不算超级高性能,因为它需要浏览器重新解析一个全新的SVG文档,但它会解决安全限制。 把它想象<object>成一个单向黑洞,可以接收SVG信息来渲染,但不会暴露任何信息。然而,这不是一个信息问题,因为你得到的只是你提供的信息<object>:没有任何东西contentDocument可以告诉你,你还不知道。 但是,如果想通过将侦听器附加到在您的主页上执行代码的SVG结构中的组件来使SVG内的组件交互,我认为这种方法不会奏效。<object>和它周围的页面之间的分隔与 有一样的嵌入关系<iframe>
用户回答回答于

由于对象标签在HTML文档中定义了嵌入对象,因此它不是文档本身的一部分,因此必须像框架一样尊重CORS

对象标签的内容被认为是外部资源

HTML元素表示一个外部资源,可以将其视为图像,嵌套的浏览上下文或插件处理的资源。

所属标签

可能回答问题的人

  • 不吃貓的鱼oo

    5 粉丝466 提问6 回答
  • Richel

    8 粉丝0 提问4 回答
  • 人生的旅途

    10 粉丝484 提问3 回答
  • 云服务小能手

    0 粉丝0 提问2 回答

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励