examples javascript html dom cors

javascript - examples - svg web



¿Por qué generar dinámicamente un SVG usando HTMLObjectElement lleva a un error de Origen cruzado? (2)

Considere el siguiente fragmento de código 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);

(Vea este JSFiddle para un ejemplo completo)

Cuando esto se ejecuta, se da el siguiente error en la consola (Google Chrome):

No se detectó DOMException: no se pudo leer la propiedad ''contentDocument'' de ''HTMLObjectElement'': se bloqueó un marco con origen " https://fiddle.jshell.net " al acceder a un marco de origen cruzado. en setTimeout ( https://fiddle.jshell.net/_display:77:19 )

Con eso en mente;

  1. ¿Por qué se considera esto como una solicitud de origen cruzado cuando se intenta acceder al contentDocument de contenido del objeto que se ha creado de forma totalmente dinámica, sin recursos externos?

  2. ¿Hay una manera de generar SVG dinámicamente de esta manera, sin ofender la política de origen cruzado de los navegadores?


Debido a que la etiqueta de objeto define un objeto incrustado dentro del documento HTML, no es parte del documento en sí, y por lo tanto debe respetar el CORS como un marco

Política del mismo origen

here se establece claramente que el contenido de la etiqueta de objeto se considera un recurso externo

El elemento HTML representa un recurso externo , que puede tratarse como una imagen, un contexto de navegación anidado o un recurso que debe ser manejado por un complemento.


El problema aquí es que los data: direcciones URL se consideran que tienen un origen único que difiere del origen del contexto que creó los data: incrustados data: contexto:

Nota: las direcciones URL de datos se tratan como orígenes opacos únicos en los navegadores modernos, en lugar de heredar el origen del objeto de configuración responsable de la navegación.

La especificación WHATWG describe html.spec.whatwg.org/multipage/… , que incluye una verificación de origen cruzado. La comparación WHATWG con el mismo origen nunca tratará un origen tradicional "tupla" puerto-host-puerto como igual a un data: "opaco" data: origen.

En su lugar, use Blob with URL.createObjectURL para generar una URL temporal del mismo origen cuyo contenido sea legible por el entorno externo:

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

No sé la razón de seguridad por la que se permite este enfoque mientras que un data: sin data: URL no lo es, pero parece que funciona. (Supongo que debido a que la URL generada es legible solo por el origen que la generó, mientras que una data: URL no sabe cómo ser legible solo por el original de su contexto original).

Tenga en cuenta también que algunas versiones de Internet Explorer son compatibles con createObjectURL pero tratan erróneamente que las URL generadas tienen un origen nulo, lo que provocaría un error en este enfoque.

Otras opciones son:

  1. No use una URL de data: lugar, sirva el contenido SVG del mismo origen que su página que crea el elemento <object> .

  2. Deshazte del <object> y del contentDocument completo y usa un elemento <svg> lugar ( fiddle ):

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

    La mayoría de los navegadores admiten elementos <svg> línea (notablemente, IE 9.0+; otros navegadores mucho antes). Esto significa que puedes hacer

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

    y solo representará el documento SVG dentro de <div> como usted esperaría.

  3. Dependiendo de lo que desee hacer con el SVG, puede cargarlo en un DOMParser y realizar una exploración / manipulación de DOM dentro del analizador.

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

    Pero el modelo DOM será separado del SVG representado en el <object> . Para cambiar el <object> , necesita serializar la estructura DOM analizada y reenviarla a la propiedad de data :

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

    Esto no parece tener un rendimiento excelente, ya que necesita que el navegador vuelva a analizar un documento SVG nuevo, pero evitará las restricciones de seguridad.

    Piense en el <object> como un agujero negro unidireccional que puede recibir información SVG para renderizar, pero no expondrá ninguna información. Sin embargo, esto no es un problema informático, ya que tiene la información que acaba de ingresar en <object> : no hay nada que contentDocument pueda decirle que aún no sepa.

    Sin embargo, si desea que los componentes dentro del SVG sean interactivos adjuntando escuchas a los componentes dentro de la estructura SVG que ejecutan el código en su página principal, no creo que este enfoque funcione. La separación entre un <object> y su página circundante tiene el mismo tipo de relación de incrustación que un <iframe> .