views:

811

answers:

2

The scene: I'm writing an embeddable widget. It takes the form of a <script> tag, which builds an iframe containing everything it needs to display. The iframe has no src, and the script writes to it with theIframe.contentWindow.document.write(). This keeps the widget contained, and keeps element ids and script from conflicting with the page on which the widget is embedded.

The trick: The widget has to be able to change its size. To do this, it sets its containing iframe's style.height. This requires access to the outer page's DOM. In Firefox and IE, this is allowed, because the iframe's document and the outer document are considered to share an origin.

The twist: In Safari, however, the two documents are considered not to share an origin. The inner document is considered to be at about:blank, while the outer document is clearly using a different protocol and "domain" (if blank can be considered the domain).

The question: How can I build an iframe programmatically whose document Safari/WebKit will consider to have the same origin as the document of the window creating it?


Edit: After further experimentation, I can't find a way to programmatically create an iframe whose location is not about:blank regardless of whether I change its contents.

If I create the frame with document.createElement(), give it a src which points to a real HTML resource on the same origin called "foo.html", and document.body.appendChild() it, Safari's console shows the element as expected in the DOM, but the contents of the page do not appear, and the document is listed in the sidebar as "about:blank".

If I include the HTML for the iframe directly in the page, the contents of foo.html appear, and "foo.html" appears in the sidebar.

If I insert the HTML using document.write(), I get the same result as with document.body.appendChild().

Both programmatic versions work in Firefox.

+1  A: 

The best suggestion I could give is to have the iframe set to a blank page on the same server (ie blank.html) and then edit the content. A pain in the rear, I know but it's a workaround.

You could also try

iframe.contentDocument.open("replace");
iframe.contentDocument.write("<b>This is some content</b>");
iframe.contentDocument.close();

However, I'm not sure if that only works in IE. Sorry I couldn't be more helpful than that.

Andy E
Good thoughts. The problem seems to be tricker than I thought, though. See the additions in the question.
Peeja
A: 

Aha. This seems to be a bug in WebKit. When an iframe is created programmatically, its src attribute is ignored. Instead, the frame defaults to about:blank and must be directed to a URL to point elsewhere. For example:

theIframe.contentWindow.location = theIframe.src
Peeja
I need a bit of clarification. Where would this javascript need to be executed, in the parent window or in the iframe itself? What is theIframeContentWindow? If you are referring to the window object of the iframe, wouldn't that just be "theIframe"? Meaning you could simplify that to "theIframe.location = theIframe.src"?Any help would be appreciated, as I've run into this exact bug. Thanks.
3n
Sorry, I was missing a dot. theIframe here is the iframe element, not the frame, or window, which it contains. theIframe.contentWindow is its window object which hosts the inner page.This code runs in the outer page, just after the iframe is added to the DOM. This is the point at which the contentWindow first exists (I believe). It's also the point at which other browsers do exactly this: navigate the iframe's window to the iframe's src.
Peeja