Error de JavaScript "Acceso denegado" al intentar acceder al objeto de documento de un <iframe> creado mediante programación (solo IE)

80

Tengo un proyecto en el que necesito crear un elemento <iframe> usando JavaScript y agregarlo al DOM. Después de eso, necesito insertar algo de contenido en el <iframe>. Es un widget que se integrará en sitios web de terceros.

No configuro el atributo "src" del <iframe> porque no quiero cargar una página; más bien, se usa para aislar / aislar el contenido que inserto en él para que no me encuentre con conflictos de CSS o JavaScript con la página principal. Estoy usando JSONP para cargar contenido HTML desde un servidor e insertarlo en este <iframe>.

Esto funciona bien, con una excepción seria: si la propiedad document.domain está configurada en la página principal (que puede ser en ciertos entornos en los que se implementa este widget), Internet Explorer (probablemente todas las versiones, pero he confirmado en 6, 7 y 8) me da un error de "Acceso denegado" cuando intento acceder al objeto de documento de este <iframe> que he creado. No sucede en ningún otro navegador en el que haya probado (todos los principales modernos).

Esto tiene cierto sentido, ya que soy consciente de que Internet Explorer requiere que configure el dominio de documento de todas las ventanas / marcos que se comunicarán entre sí con el mismo valor. Sin embargo, no conozco ninguna forma de establecer este valor en un documento al que no puedo acceder.

¿Alguien conoce alguna forma de hacer esto, de alguna manera establecer la propiedad document.domain de este <iframe> creado dinámicamente? ¿O no lo estoy viendo desde el ángulo correcto? ¿Hay otra manera de lograr lo que busco sin encontrarme con este problema? Necesito usar un <iframe> en cualquier caso, ya que la ventana aislada / en espacio aislado es crucial para la funcionalidad de este widget.

Aquí está mi código de prueba:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>Document.domain Test</title>
    <script type="text/javascript">
      document.domain = 'onespot.com'; // set the page's document.domain
    </script>
  </head>
  <body>
    <p>This is a paragraph above the &lt;iframe&gt;.</p>
    <div id="placeholder"></div>
    <p>This is a paragraph below the &lt;iframe&gt;.</p>
    <script type="text/javascript">
      var iframe = document.createElement('iframe'), doc; // create <iframe> element
      document.getElementById('placeholder').appendChild(iframe); // append <iframe> element to the placeholder element
      setTimeout(function() { // set a timeout to give browsers a chance to recognize the <iframe>
        doc = iframe.contentWindow || iframe.contentDocument; // get a handle on the <iframe> document
        alert(doc);
        if (doc.document) { // HEREIN LIES THE PROBLEM
          doc = doc.document;
        }
        doc.body.innerHTML = '<h1>Hello!</h1>'; // add an element
      }, 10);
    </script>
  </body>
</html>

Lo he alojado en:

http://troy.onespot.com/static/access_denied.html

Como verá si carga esta página en IE, en el momento en que llamo a alert (), tengo un identificador en el objeto de ventana del <iframe>; Simplemente no puedo profundizar más en su objeto de documento.

¡Muchas gracias por cualquier ayuda o sugerencia! Estaré en deuda con quien pueda ayudarme a encontrar una solución a esto.

Estropear
fuente
El enlace de troy.onespot está muerto.
mbomb007

Respuestas:

67

si la propiedad document.domain está configurada en la página principal, Internet Explorer me da un "Acceso denegado"

Suspiro. Sí, es un problema de IE (¿error? Es difícil de decir ya que no existe un estándar documentado para este tipo de molestias). Cuando crea un iframe sin src, recibe un document.domaindel documento principal en location.hostlugar de su document.domain. En ese punto, casi has perdido, ya que no puedes cambiarlo.

Una solución horrenda es establecer srcun javascript: URL (¡urgh!):

 iframe.src= "javascript:'<html><body><p>Hello<\/p><script>do things;<\/script>'";

Pero por alguna razón, un documento de este tipo no puede establecer el suyo document.domaindesde un script en IE (el viejo "error no especificado"), por lo que no puede usarlo para recuperar un puente entre el padre (*). Usted podría utilizarlo para escribir todo el documento HTML, suponiendo que el widget no necesita hablar con su documento de nivel superior una vez que se crea una instancia.

Sin embargo, las URL de JavaScript de iframe no funcionan en Safari, por lo que aún necesitaría algún tipo de rastreo del navegador para elegir qué método usar.

*: Por alguna otra razón, puede , en IE, establecer document.domaindesde un segundo documento, documento escrito por el primer documento. Entonces esto funciona:

if (isIE)
    iframe.src= "javascript:'<script>window.onload=function(){document.write(\\'<script>document.domain=\\\""+document.domain+"\\\";<\\\\/script>\\');document.close();};<\/script>'";

En este punto, el nivel de horror es demasiado alto para mí, estoy fuera. Haría el HTML externo como dijo David.

bobince
fuente
10
@bobince: ¡Eres increíble! De hecho, me topé con este mismo enfoque anoche después de mucho más buscar en Google. De hecho, creo que podría haber encontrado una solución entre navegadores incluso más robusta y potencialmente menos complicada: telerik.com/community/forums/aspnet-ajax/editor/… - observe la publicación de Jeff Tucker del 21 de agosto. El atributo "src" de iframe> para "javascript: void ((function () {document.open (); document.domain = \ 'tld.com \'; document.close ();}) ())" parece para hacer el truco, y en una forma de navegador cruzado. También funciona en Safari (al menos v3 +).
Bungle
1
Aquí hay una página de prueba que ilustra mi comentario anterior: troy.onespot.com/static/access_denied_test.html
Bungle
1
¡Oh, genial! ¡Eso está mucho mejor! Es interesante que hacerlo directamente debería funcionar ... normalmente, un javascript: URL se ejecutaría en el contexto de su padre, pero ese parece no ser el caso con iframe src. Probablemente también puedas perder la void()llamada, ya que la función ya regresa undefined.
Bobince
1
Por cierto, obtiene un error al volver a cargar la página en IE con esto, ya que IE intenta retener la ubicación del iframe ... argh. No sé si hay una forma de evitar eso.
Bobince
1
@bobince: Oh hombre, tienes razón. Gracias por señalar eso; Estaba tan emocionado de que funcionó la primera vez que no me molesté en recargar. ¿Qué quiere decir IE tratando de retener la ubicación de <iframe>? Tengo que encontrar una solución a esto, así que lo mantendré informado; haga lo mismo si encuentra algo.
Bungle
18

Bueno, sí, la excepción de acceso se debe al hecho de que document.domaindebe coincidir en su padre y en su iframe, y antes de que lo hagan, no podrá establecer mediante programación la document.domainpropiedad de su iframe.

Creo que su mejor opción aquí es apuntar la página a una plantilla propia:

iframe.src = '/myiframe.htm#' + document.domain;

Y en myiframe.htm:

document.domain = location.hash.substring(1);
David Hedlund
fuente
3
Gracias, David. Aprecio lo que parece ser una sugerencia sólida, pero prefiero no adoptar este enfoque a menos que sea un último recurso. Si lo entiendo correctamente, la plantilla debería vivir en el mismo dominio que la página principal (¿es así?), Y eso complicaría la implementación para nuestros clientes. Idealmente, la implementación debería ser tan simple como insertar algunas líneas de JavaScript en el HTML de sus páginas.
Bungle
1
Sí, la solución requiere otro archivo en ese mismo dominio. Sin embargo, me temo que eso es lo mejor que se me ocurre.
David Hedlund
1
@Bungle: Creo que es tu única opción.
Tim Down
3

bueno, en realidad tengo un problema muy similar, pero con un giro ... digamos que el sitio de nivel superior es a.foo.com - ahora configuro el dominio del documento en a.foo.com

luego, en el iframe que creo / tengo, también lo configuro a.foo.com

tenga en cuenta que no puedo configurarlos también foo.com porque hay otro iframe en la página que apunta a bafoo.com (que nuevamente usa a.foo.com pero no puedo cambiar el código del script allí)

notará que esencialmente estoy configurando document.domain a lo que ya sería de todos modos ... pero tengo que hacer eso para acceder al otro iframe que mencioné de bafoo.com

dentro de mi marco, después de configurar el dominio, aunque todos los iframes tienen la misma configuración, sigo recibiendo un error al llegar al padre en IE 6/7

hay otras cosas que son realmente bizaree

en el nivel exterior / superior, si espero su evento de carga y configuro un temporizador, eventualmente puedo alcanzar el marco al que necesito acceder ... pero nunca puedo alcanzar de abajo hacia arriba ... y realmente Necesito poder

Además, si configuro todo para que sea foo.com (que, como dije, no puedo hacer) ¡FUNCIONA! pero por alguna razón, cuando se usa el mismo valor que location.host ... no lo hace y me está matando ...

Sean
fuente
2

Solo lo uso <iframe src="about:blank" ...></iframe>y funciona bien.

jlcooke
fuente
2

para IE, el puerto importa. Entre dominios, debería ser el mismo puerto.

lwpro2
fuente
1

¿Has probado jQuery.contents () ?

Deniss Kozlovs
fuente
1
Deniss, buena sugerencia, y gracias - le di una oportunidad (ver < troy.onespot.com/static/access_denied_jquery.html> ) pero obtuve un error similar; esta vez es "Permiso denegado" dentro del script jQuery. Sospecho que es el mismo obstáculo o uno similar.
Bungle
1
Lo siento, esa URL se estropeó. Prueba: troy.onespot.com/static/access_denied_jquery.html
Bungle
4
¿Por qué jQuery tendría acceso mágico al documento del iframe?
Tim Down
8
@Tim Down: No creo que la suposición fuera que jQuery tendría acceso mágico; más bien, jQuery a menudo tiene algunos trucos ingeniosos bajo la manga para resolver problemas entre navegadores y es posible que ya haya implementado una solución alternativa. Estoy de acuerdo en que no presagia nada bueno, pero creo que fue una buena sugerencia y vale la pena intentarlo.
Bungle
Esto realmente no es una respuesta. Es solo una sugerencia y probablemente sería mejor como comentario en la publicación original.
Neil Monroe
1

Parece que el problema con IE surge cuando intenta acceder al iframe a través del objeto document.frames; si almacena una referencia al iframe creado en una variable, entonces puede acceder al iframe inyectado a través de la variable (my_iframe en el código siguiente ).

Conseguí que esto funcione en IE6 / 7/8

var my_iframe;
var iframeId = "my_iframe_name"
if (navigator.userAgent.indexOf('MSIE') !== -1) {
  // IE wants the name attribute of the iframe set
  my_iframe = document.createElement('<iframe name="' + iframeId + '">');
} else {
  my_iframe = document.createElement('iframe');
}

iframe.setAttribute("src", "javascript:void(0);");
iframe.setAttribute("scrolling", "no");
iframe.setAttribute("frameBorder", "0");
iframe.setAttribute("name", iframeId);

var is = iframe.style;
is.border = is.width = is.height = "0px";

if (document.body) {
  document.body.appendChild(my_iframe);
} else {
  document.appendChild(my_iframe);
}
usuario299340
fuente
1
Gracias. Esto parece haberme solucionado el problema.
vit
1
Lo extraño es que esto era lo mismo que estaba usando. estaba funcionando bien, ahora, de repente, sigo obteniendo errores de acceso denegado
frostymarvelous
1

Tuve un problema similar y mi solución fue este fragmento de código (probado en IE8 / 9, Chrome y Firefox)

var iframe = document.createElement('iframe');
document.body.appendChild(iframe);

iframe.src = 'javascript:void((function(){var script = document.createElement(\'script\');' +
  'script.innerHTML = "(function() {' +
  'document.open();document.domain=\'' + document.domain +
  '\';document.close();})();";' +
  'document.write("<head>" + script.outerHTML + "</head><body></body>");})())';

iframe.contentWindow.document.write('<div>foo</div>');

He probado varios métodos, pero este parece ser el mejor. Puedes encontrar algunas explicaciones en la publicación de mi blog aquí .

Zoltan Kochan
fuente
1

Siguiendo el método extremadamente simple de Andralor aquí solucionó el problema para mí: https://github.com/fancyapps/fancyBox/issues/766

Básicamente, vuelva a llamar al iframe onUpdate:

$('a.js-fancybox-iframe').fancybox({
    type: 'iframe',
    scrolling : 'visible',
    autoHeight: true,
    onUpdate: function(){
     $("iframe.fancybox-iframe");
   }
 });
Tim Denison
fuente
-2

IE funciona con iframe como todos los demás navegadores (al menos para las funciones principales). Solo tienes que seguir un conjunto de reglas:

  • antes de cargar cualquier javascript en el iframe (esa parte de js que necesita saber sobre el iframe padre), asegúrese de que el padre haya cambiado document.domain.
  • cuando se cargan todos los recursos de iframe, cambie document.domain para que sea el mismo que el definido en parent. (Debe hacer esto más tarde porque la configuración del dominio hará que falle la solicitud del recurso iframe)

  • ahora puede hacer una referencia para la ventana principal: var winn = window.parent

  • ahora puede hacer una referencia al HTML padre para manipularlo: var parentContent = $ ('html', winn.document)
  • en este punto, debería tener acceso a la ventana / documento principal de IE y puede cambiarlo como no lo desee
Ionut Tocila
fuente
-11

Para mí, encontré que la mejor respuesta era verificar los permisos de archivo a los que se les niega el acceso.

Acabo de actualizar a jQuery-1.8.0.js y recibí el error Acceso denegado en IE9.

Desde el Explorador de Windows

  • Hice clic derecho en el archivo seleccionado Propiedades
  • Seleccionó la pestaña de seguridad
  • Hizo clic en el botón Avanzado
  • Seleccionó la pestaña Propietario
  • Haga clic en el botón Editar
  • Administradores seleccionados (MachineName \ Administrators)
  • Haga clic en Aplicar
  • Cerró todas las ventanas.

Probó el sitio. No más problema.

Tuve que hacer lo mismo con el script jQuery-UI que acababa de actualizar también

Encapuchado
fuente
2
Creo que estás resolviendo un problema diferente.
Danyal Aytekin
5
El OP está construyendo un widget de terceros. No puede esperar que todos los visitantes sigan todos estos pasos para que se muestre el widget.
Christophe