Błąd JavaScript „Odmowa dostępu” podczas próby uzyskania dostępu do obiektu dokumentu utworzonego programowo elementu <iframe> (tylko IE)

80

Mam projekt, w którym muszę utworzyć element <iframe> przy użyciu JavaScript i dołączyć go do DOM. Następnie muszę wstawić zawartość do <iframe>. To widżet, który zostanie osadzony w witrynach innych firm.

Nie ustawiam atrybutu „src” elementu <iframe>, ponieważ nie chcę ładować strony; służy raczej do izolowania / piaskownicy zawartości, którą do niego wstawiam, aby nie napotkać konfliktów CSS lub JavaScript ze stroną nadrzędną. Używam JSONP do załadowania zawartości HTML z serwera i wstawienia jej do tego <iframe>.

Mam to dobrze, z jednym poważnym wyjątkiem - jeśli właściwość document.domain jest ustawiona na stronie nadrzędnej (co może być w niektórych środowiskach, w których ten widget jest wdrażany), Internet Explorer (prawdopodobnie wszystkie wersje, ale mam potwierdzone w 6, 7 i 8) wyświetla błąd „Odmowa dostępu”, gdy próbuję uzyskać dostęp do obiektu dokumentu tej <iframe>, którą utworzyłem. Nie dzieje się tak w innych przeglądarkach, w których testowałem (we wszystkich głównych współczesnych).

Ma to sens, ponieważ zdaję sobie sprawę, że Internet Explorer wymaga ustawienia document.domain wszystkich okien / ramek, które będą się ze sobą komunikować, na tę samą wartość. Jednak nie znam żadnego sposobu na ustawienie tej wartości w dokumencie, do którego nie mam dostępu.

Czy ktoś wie, jak to zrobić - ustawić w jakiś sposób właściwość document.domain tej dynamicznie utworzonej <iframe>? A może nie patrzę na to pod odpowiednim kątem - czy jest inny sposób na osiągnięcie tego, do czego zmierzam, bez napotkania tego problemu? W każdym przypadku muszę używać elementu <iframe>, ponieważ izolowane / piaskownica ma kluczowe znaczenie dla funkcjonalności tego widżetu.

Oto mój kod testowy:

<!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>

Hostowałem to pod adresem:

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

Jak zobaczysz, jeśli załadujesz tę stronę w IE, w miejscu, w którym wywołam alert (), mam uchwyt do obiektu okna <iframe>; Po prostu nie mogę wejść głębiej w obiekt dokumentu.

Bardzo dziękuję za pomoc lub sugestie! Będę wdzięczny komukolwiek, kto pomoże mi znaleźć rozwiązanie tego problemu.

Klecić
źródło
Link do troy.onespot jest martwy.
mbomb007

Odpowiedzi:

67

jeśli właściwość document.domain jest ustawiona na stronie nadrzędnej, program Internet Explorer wyświetla komunikat „Odmowa dostępu”

Westchnienie. Tak, jest to problem z IE (błąd? Trudno powiedzieć, ponieważ nie ma udokumentowanego standardu dla tego rodzaju nieprzyjemności). Kiedy tworzysz ramkę iframe bez źródła, otrzymuje ona document.domainz dokumentu nadrzędnego location.hostzamiast swojego document.domain. W tym momencie straciłeś prawie wszystko, ponieważ nie możesz tego zmienić.

Przerażającym obejściem jest ustawienie srcjavascript: URL (urgh!):

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

Ale z jakiegoś powodu taki dokument nie może ustawić własnego document.domainze skryptu w IE (stary dobry „nieokreślony błąd”), więc nie możesz go użyć do odzyskania pomostu między rodzicem (*). Państwo mogli używać go napisać cały dokument HTML, zakładając widżet nie trzeba mówić do swojego dokumentu macierzystego po jego wystąpienia.

Jednak adresy URL JavaScript iframe nie działają w Safari, więc nadal potrzebujesz jakiegoś rodzaju przeglądarki, aby wybrać metodę do użycia.

*: Z jakiegoś innego powodu, to może , w IE, ustawiony document.domainz drugim dokumencie, document.written przez pierwszego dokumentu. Więc to działa:

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

W tym momencie poziom ohydności jest dla mnie zbyt wysoki, wypadam. Zrobiłbym zewnętrzny HTML, tak jak powiedział David.

bobince
źródło
10
@bobince: Jesteś niesamowity! Właściwie natknąłem się na to podejście późno wczoraj wieczorem po znacznie większej liczbie wyszukiwań w Google. W rzeczywistości wydaje mi się, że mogłem znaleźć jeszcze bardziej niezawodne i potencjalnie mniej kludgy rozwiązanie dla różnych przeglądarek: telerik.com/community/forums/aspnet-ajax/editor/ ... - zwróć uwagę na post Jeffa Tuckera z 21 sierpnia. iframe> „src” atrybut do „javascript: void ((function () {document.open (); document.domain = \ 'tld.com \'; document.close ();}) ())" wydaje się załatwić sprawę i to w różnych przeglądarkach. Działa również w Safari (co najmniej v3 +).
Bungle
1
Oto strona testowa ilustrująca mój poprzedni komentarz: troy.onespot.com/static/access_denied_test.html
Bungle,
1
Świetnie! To jest o wiele lepsze! Ciekawe, że zrobienie tego bezpośrednio powinno zadziałać ... normalnie javascript: URL byłby wykonywany w kontekście swojego rodzica, ale wydaje się, że tak nie jest w przypadku iframe src. Prawdopodobnie możesz również stracić void()połączenie, ponieważ funkcja już zwraca undefined.
bobince
1
Nawiasem mówiąc, podczas ponownego ładowania strony w IE pojawia się błąd, ponieważ IE próbuje zachować lokalizację ramki iframe ... argh. Nie wiem, czy jest na to sposób.
bobince
1
@bobince: O rany, masz rację. Dzięki za wskazanie tego; Byłem tak podekscytowany, że zadziałało za pierwszym razem, że nie zawracałem sobie głowy ponownym ładowaniem. Co masz na myśli, mówiąc, że IE próbuje zachować lokalizację <iframe>? Muszę znaleźć rozwiązanie tego problemu, więc będę Cię informować - zrób to samo, jeśli coś znajdziesz.
Bungle
18

Cóż, tak, wyjątek dostępu wynika z faktu, że document.domainmusi pasować do elementu nadrzędnego i elementu iframe, a zanim to zrobią, nie będziesz w stanie programowo ustawić document.domainwłaściwości elementu iframe.

Myślę, że najlepszą opcją jest wskazanie strony na własny szablon:

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

A w myiframe.htm:

document.domain = location.hash.substring(1);
David Hedlund
źródło
3
Dzięki, Davidzie - doceniam to, co wydaje się solidną sugestią, ale wolałbym nie stosować takiego podejścia, chyba że jest to ostateczność. Jeśli dobrze rozumiem, szablon musiałby znajdować się w tej samej domenie co strona nadrzędna (czy to prawda?), Co skomplikowałoby implementację dla naszych klientów. Idealnie byłoby, gdyby implementacja była tak prosta, jak wstawienie kilku wierszy kodu JavaScript do kodu HTML ich stron.
Bungle
1
Tak, rozwiązanie wymaga innego pliku w tej samej domenie. Obawiam się, że to najlepsze, co mogę wymyślić.
David Hedlund
1
@Bungle: Myślę, że to twoja jedyna opcja.
Tim Down
3

cóż, mam bardzo podobny problem, ale z niespodzianką ... powiedzmy, że witryna najwyższego poziomu to a.foo.com - teraz ustawiłem domenę dokumentu na a.foo.com

następnie w utworzonej / posiadanej przeze mnie ramce iframe również ustawiam ją jako a.foo.com

zwróć uwagę, że nie mogę ich też ustawić foo.com b / c na stronie wskazującej na bafoo.com znajduje się inna ramka iframe (która ponownie używa a.foo.com, ale nie mogę tam zmienić kodu skryptu)

zauważysz, że zasadniczo ustawiam document.domain na to, co i tak już by było ... ale muszę to zrobić, aby uzyskać dostęp do drugiej ramki iframe, o której wspomniałem z bafoo.com

wewnątrz mojej ramki, po ustawieniu domeny, chociaż wszystkie elementy iframe mają to samo ustawienie, nadal pojawia się błąd podczas sięgania do rodzica w IE 6/7

są inne rzeczy, które są naprawdę bizaree

na zewnętrznym / najwyższym poziomie, jeśli poczekam na zdarzenie onload i ustawię licznik czasu, w końcu mogę sięgnąć do ramki, do której muszę uzyskać dostęp .... ale nigdy nie mogę sięgnąć od dołu do góry ... i naprawdę trzeba umieć

również jeśli ustawię wszystko na foo.com (czego, jak powiedziałem, nie mogę zrobić) TO DZIAŁA! ale z jakiegoś powodu, gdy używasz tej samej wartości co location.host .... nie działa i jego cholernie mnie zabija .....

Sean
źródło
2

Po prostu używam <iframe src="about:blank" ...></iframe>i działa dobrze.

jlcooke
źródło
2

dla IE port ma znaczenie. Pomiędzy domenami powinien to być ten sam port.

lwpro2
źródło
1

Czy próbowałeś już jQuery.contents () ?

Deniss Kozlovs
źródło
1
Deniss, dobra sugestia i dziękuję - dałem temu szansę (patrz < troy.onespot.com/static/access_denied_jquery.html> ), ale otrzymałem podobny błąd; tym razem jest to „Odmowa uprawnień” w skrypcie jQuery. Podejrzewam, że to ta sama lub podobna blokada.
Bungle
1
Przepraszamy, ten adres URL został zniekształcony. Wypróbuj: troy.onespot.com/static/access_denied_jquery.html
Bungle,
4
Dlaczego jQuery miałby magiczny dostęp do dokumentu iframe?
Tim Down
8
@Tim Down: Nie sądzę, aby założenie było takie, że jQuery miałby magiczny dostęp; raczej jQuery często ma kilka sprytnych sztuczek w rękawie, aby rozwiązać problemy z różnymi przeglądarkami, i może już zaimplementował obejście tego problemu. Zgadzam się, że to nie wróżyło dobrze, ale myślę, że to była dobra sugestia i warto spróbować.
Bungle
To naprawdę nie jest odpowiedź. To tylko sugestia i prawdopodobnie byłby lepszy jako komentarz do oryginalnego postu.
Neil Monroe
1

Wygląda na to, że problem z IE pojawia się, gdy próbujesz uzyskać dostęp do elementu iframe za pośrednictwem obiektu document.frames - jeśli przechowujesz odniesienie do utworzonej ramki iframe w zmiennej, możesz uzyskać dostęp do wstrzykniętego elementu iframe za pośrednictwem zmiennej (my_iframe w kodzie poniżej ).

Mam to do pracy w 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);
}
user299340
źródło
1
Dzięki. Wydaje się, że to rozwiązało problem.
wit.
1
dziwne jest to, że to była ta sama rzecz, której używałem. działało dobrze, teraz nagle, ciągle otrzymuję odmowę dostępu do błędów
frostymarvellous
1

Miałem podobny problem, a moim rozwiązaniem był ten fragment kodu (testowany w IE8 / 9, Chrome i 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>');

Wypróbowałem kilka metod, ale ta okazała się najlepsza. Możesz znaleźć kilka wyjaśnień w moim wpisie na blogu tutaj .

Zoltan Kochan
źródło
1

Postępowanie zgodnie z niezwykle prostą metodą z Andralor rozwiązało problem dla mnie: https://github.com/fancyapps/fancyBox/issues/766

Zasadniczo wywołaj element iframe ponownie onUpdate:

$('a.js-fancybox-iframe').fancybox({
    type: 'iframe',
    scrolling : 'visible',
    autoHeight: true,
    onUpdate: function(){
     $("iframe.fancybox-iframe");
   }
 });
Tim Denison
źródło
-2

IE działa z iframe jak wszystkie inne przeglądarki (przynajmniej dla głównych funkcji). Musisz tylko przestrzegać zestawu zasad:

  • zanim załadujesz jakikolwiek skrypt javascript w iframe (ta część js, która musi wiedzieć o elemencie nadrzędnym iframe), upewnij się, że rodzic zmienił document.domain.
  • kiedy wszystkie zasoby iframe zostaną załadowane, zmień document.domain na taką samą, jak zdefiniowana w parent. (Musisz to zrobić później, ponieważ ustawienie domeny spowoduje niepowodzenie żądania zasobu iframe)

  • teraz możesz utworzyć odniesienie do okna nadrzędnego: var winn = window.parent

  • teraz możesz utworzyć odniesienie do nadrzędnego kodu HTML, aby nim manipulować: var parentContent = $ ('html', winn.document)
  • w tym momencie powinieneś mieć dostęp do okna / dokumentu nadrzędnego IE i możesz go zmienić tak, jak chcesz
Ionut Tocila
źródło
-11

Dla mnie stwierdziłem, że lepszą odpowiedzią jest sprawdzenie uprawnień do pliku, do których odmawia się dostępu.

Właśnie zaktualizowałem do jQuery-1.8.0.js i otrzymałem błąd odmowy dostępu w IE9.

Z Eksploratora Windows

  • Kliknąłem prawym przyciskiem myszy plik i wybrałem Właściwości
  • Wybrano kartę Bezpieczeństwo
  • Kliknięto przycisk Zaawansowane
  • Wybrano kartę Właściciel
  • Kliknięto przycisk Edytuj
  • Wybrani administratorzy (nazwa_komputera \ administratorzy)
  • Kliknięto Zastosuj
  • Zamknięte wszystkie okna.

Przetestowano witrynę. Koniec z problemem.

Musiałem zrobić to samo dla skryptu jQuery-UI, który właśnie zaktualizowałem

Zakapturzony
źródło
2
Myślę, że rozwiązujesz inny problem.
Danyal Aytekin
5
OP tworzy widget innej firmy. Nie możesz oczekiwać, że każdy odwiedzający wykona wszystkie te kroki, aby wyświetlić widżet.
Christophe,