Auto-Sizing <iframe>s?

OK, here’s a technical problem to solve. I’ve been chatting with one of the administrators of a very large website (if you guess, keep it to yourself) which, as part of its function, displays user-provided content in the middle of site-provided pages. For historical reasons, the user-provided content needs to allow scripts, Flash and other dynamic content. However, they want to prevent such content affecting “their” part of the page, above and below the user content, and performing malicious actions. Currently, this can be done using either script or CSS-positioning or both.

I suggested that in general, their problem was unsolvable, but that the industrial-strength way of getting such separation was to use an <iframe>. He told me that they’d tried that, but as scrollbars internal to the page were unacceptable, they’d had to try resizing the iframe dynamically using JS to eliminate the bars. This solution had run into technical difficulties, as explained in the following saga, some of which I don’t quite follow.

Read it for background if you think it helps – but the question is: how hard would it be to get an <iframe> or <object> which always resized to its content? The solution can involve either JavaScript, or hacking Gecko.

Currently in Gecko, as far as I can tell, if you don’t set a width and a height, <object> tags of type e.g. image/png size to their content. So why can’t (or why don’t) <object> tags of type text/html?

Here’s the story:

Here was the general process under which we went through to implement iframes:

The Problem: The users enter malicious content on the page that can overwrite our page content as well as steal cookies since it’s in the same domain.

Our proposed solution: We want to separate the user data on the page from our site content. The most esthetically clean way to do this was inside of an iframe that is automatically resized to avoid having embedded scrollbars which our user community would never be okay with.

How it was implemented: We wrapped the content inside of JavaScript to detect the vertical height of the page and then render the iframe appropriately to avoid any scrollbars inside the page itself. When we tried to roll this out we encountered massive problems.

  1. Any items with links, when clicked on only redirected the main page. We solved this issue for the most part, except for Applets, objects, etc… We would be willing to take this hit and force our developer community to change their code to support this. This is somewhere around 10-30% (the number is hard to calculate since it isn’t a part of the page, it’s a part of the code hosted elsewhere).
  2. Tables rendered badly. This ended up being the show-stopper. Users would put dozens of large images into tables and wouldn’t put sizes on the images. This would cause the page to sit there for minutes in some cases because the table wouldn’t finish loading so our JavaScript wouldn’t render the entire table until every last image were loaded. So next we attempted to change it so that every image pre-loaded as a 1×1 pixel image. That worked okay, but the only way to do that was to wrap the entire user’s content in <noscript> tags. The problem with that was that once we encountered an end </noscript> tag it would start rendering again. IE:

    <noscript>
    User content
    <script>
    //Some javascript
    </script>
    <noscript>
    You don’t support JS
    </noscript> <— at this point it breaks our <noscript> block
    More user content with tables, etc which breaks our code…
    </noscript>

    At the time <noscript> was found on 22% of user input. Now it is closer to 2-3% due to some changes we have made, although that number is still too high to implement this solution. The users would also do really bizarre things with their code like have weirdly malformed tables, and JavaScript that outputted </noscript> tags. The fact that there was no way to completely stop text from rendering so that we could pause it and run JS over the code made this approach unfeasible after months of testing.

  3. It broke save-as in IE, which we don’t care about that much, but it was a big deal in certain countries, as users really like to save data to their desktops to prove that they saw it and agreed to whatever was said on the page. We can get around this issue by allowing a save-as friendly version that can only be linked to from our own page, but it’s less than ideal.
  4. Certain forms of popups still allowed some forms of overwriting (both Netscape and IE verified – although SP2 has fixed some of these issues since that time).
  5. When users resized during render time it became an issue because of the weird way in which the JS was written… it would be making assumptions about a size that was no longer accurate.
  6. It caused JS errors with pages that attempted to resize themselves.
  7. Browser support – with iframes and JS required for this to work it only supported ~90% of browsers.

Based on our discussion the right idea would be to have a different form of iframe that resizes to the size of the page it is calling to avoid the embedded scrollbar issue. If we could make this a standard we might even be able to get higher than the ~90% we found in #7 of the problems list.

24 thoughts on “Auto-Sizing <iframe>s?

  1. Gerv, see bug 80713.

    I’ve looked into this a few times, but there are some fundamental issues here…

    1) The CSS layout model depends on there being a well-defined initial containing block (the viewport). This is needed for things like fixed and absolute positioning. It’s not clear how one could provide this with an iframe that’s sizing “intrinsically”. What is the intrinsic size of an iframe which has a in it? Or does that div not affect the size? In that case, you get scrollbars.

    2) The CSS layout model depends on there being a well-defined width for block layout. If one is assumed, the blocks will happily lay out to that width by default. So with CSS, meaningful intrinsic sizing horizontally is hard. Even if this is done (via some sort of setup like width shrink-wrapping, with a max width set at the current containing block width in the parent), you still have an issue with scrollbars. Consider , for example….

  2. Damn. Why can’t blogs just post in plaintext instead of eating input?

    The first example was:

    <div style=”position: absolute; left: 0; right: -10px”>

    The second example was:

    <body style=”width: 200%”>

  3. Here is an idea that will only work of Moz:

    Put the site content into an xbl, with a children tag where the user content must go. Attach the xbl to the body tag. Site content is then anonymous, and hence not accessible by user content.

    I think you may be able to achive the same result with a IE htc. Try setting literalContent=”false”. See PUBLIC:COMPONENT Element (MSDN)

  4. I’d like to know how they got the iframe to dynamically resize even when wrapped in JS. I’ve been trying to do exactly that, and I’ve been having a terrible time figuring out how to determine the size of the internal content.

    So, I take it that the iframe can’t access ownerDocument.iframe_id and set its width and height. I don’t know that, I just would assume that somebody’s already tried that.

    -Max

  5. gandalf: Just “Save as complete page” any moderately-complicated and reasonably large web page, and then create a new page with an <object> tag with the first page as the “data” attribute.

    Boris: I think it would be reasonable for the iframe to just do “overflow: hidden” for your first example (i.e. you just can’t see the parts which are outside.)

    The solution probably doesn’t have to work in pathological cases – the company concerned can, to a certain extent, just tell its users not to do things like <body width=”200%”>; if they have no useful use other than breaking the page, they’ll be OK with that.

    Given that we’re at an edge case in standards terms, best-efforts would be OK.

    Max: good point. I don’t actually know if they did try that. It would mean you couldn’t totally restrict JS in the <iframe> from accessing the parent page (via mechanisms yet to be invented). But that might not be a showstopper.

  6. I’m not sure if this is what you need, but have you tried placing the iframe inside a div and set the iframe width and height to “100%” both inline and on CSS, then resize the outside DIV using script, this has worked for me personally in creating a resizable mini-browser inside of a webpage.

  7. I tried to do the same thing way back when, when the sidebar was new in Mozilla. I wanted it to act like 3D Studio Max’s side bar where there is one scrollbar and all of the sections can be open at once.

    With the xul box model I ran into the same problem and eventually gave up.

    Example Max interface
    The dark/light grey line on the right is essentially the scrollbar. You can pan it up and down by clicking and dragging on any non-button/textbox portion.

  8. Just go to http://www.heizen.ch/oertli-service/ , then choose “Fanartikel-Shop” (menu at the left). The green icons are inside an iframe document. Click on some of them and see how the iframe is resized, having exactly the height of the inner contents.

    They use the onload-handler of the iframe to re-set the height. For browsers not supporting the onload-handler, they use a script in the outer document that does the resize every 500ms.

    Not that I’d mislead you to copy-paste it. You’re the license guru after all :)

  9. Boris: I think we could do this without too much work. I should try hacking it up. Let’s suppose that an auto-height IFRAME just looked at the height of its child document’s scrolled view, resized itself to that height, and then resized itself to that height (plus the height of the horizontal scrollbar, if any).

    1) Yes, it wouldn’t converge when the height of the body depends on the viewport height. “don’t do that”

    2) I think for most use-cases horizontal intrinsic sizing is not desired or necessary.

  10. The problem with a JavaScript-based IFrame solution, as I see it, is that to prevent the user-provided content from interacting with site content you will have to put the user-content in a different domain. But once you do that, you lose the ability for your site to interact with the IFrame content. So I don’t even see how they thought they were going to dynamically resize the IFrame if there is no way for them to query the content to determine its size.

    Well, actually there is one way I can think of, but I can’t imagine implementing it in a live environment.

    If they were to inject javascript into the user-generated pages which made an XMLHttp call back to the server in the onload and onresize events, and then on the site-content server they would also have to make XMLHttp calls to query for that info every xxx milliseconds so it could resize the IFrame appropriately.

    I can imagine a scenario where a new HTML tag type were created or the behavior of the IFrame height attribute were modified so that it would resize to its content, but it would certainly need a wide level of consensus that that was desirable before it would ever see implementation into standards and browsers. I would be very surprised to see that anytime in the near future.

  11. So what did we figure out guys? I found it funny, after yesterday looking to solve an iframe resing issue, that you guys were on the same trip.

    I am trying to put my blog site within an iframe so that the global nave will still be visible. I dont really feel like messing wiht the php index file, and not even sure if that would do anything anyway.

    So, what i have had to do, until i see someone come up with a solution is to put a height of 2500px on the css sheet to assuer that it will accomodate for the whole page, but when you load a page with less info on it, there is a whole lot of blank space. Atleast this way the scroll bar disappears because there is nothing to scroll with the 2500px height.

    Is this the same thing you all are taling about? Please give me an idea if there is a specefic fix for my solution, maybe i am barking up the wrong tree.

    heres teh page click here

  12. Awesome! I just found this today after working for the better part of 6 hours to hack the javascript together (its not perfect, ive given up).

    Any chance you guys can also fix so an auto width would work on an inline IFRAME?

    I wrote a script that works for resizing the width, but tt would be nice to have have to use a resize script for the width.

  13. This code in function called at onload event of the iframe did most the trick for me:
    document.getElementById(‘the_iframe’).height = document.getElementById(‘the_iframe’).contentWindow.document.body.scrollHeight;

    However,

    1) it just enlarges the iframe in Fx102 and Fx103
    1.1) correctly resizes in IE55SP2

    2) on manual page reload the iframe resizes correctly

    3) onload event in iframe is not W3C-HTML401Transitional -specified, so to pass their validator more JavaScript is required

    Above code works on both of my browsers, little different than the great “dynamicindex17/iframessi_dev.htm” – and didn’t get the dynamicdrive:s example working at all (perhaps some typos on my side).

  14. Epanding on “Jussi” comments:
    I too battled this issue around and around. This positively works. Here’s my e-mail sent to our dev team on this subject matter.

    –Email Message–

    Inside the “<HEAD>” Tag:

    <script type=”text/javascript”>
    function resizeIframe() {
    parent.document.getElementById(‘source_frame_ID_Name’).height =
    document.getElementById(‘Target_frame_ID_Name’).scrollHeight;

    }
    </script>

    Note the Source & Target ID Name space. Self explanatory.

    Inside the “” you need the following: onLoad=”resizeIframe()” onResize=”resizeIframe()”

    It’ll look like this:

    <body onLoad=”resizeIframe()” onResize=”resizeIframe()”>

    Keep in mind, each successive page “INHERITS” the iframe/div size since the code asks for the parent size…This is what got a lot of us in trouble. Meaning, if A.asp is 800x ? Long, then B.asp onload will inherit whatever the length of the page was on A and resize itself to that length. The DIV scrolls will be gone, just the page will be long.

    The other thing is the source iFrame or layer (must for iFrame) must have overflow set to hidden. eg. overflow: hidden;
    And all references to HEIGHT must be gone. That includes CSS tags.

    This solves 99.99 percent of the browser problem.

    –END Email–

  15. Thanks T.Thomas, onResize was one thing I was missing, but I got to work harder to get the iframe to make it’s size smaller too. Perhaps I have too much height definitions or something.

    I found that if I put the DOCTYPE-definition
    in the beginning of the iframe:d document, the iframe appears as blank!

    Similar think happens if I put
    style=”overflow: hidden;”
    as an attribute to iframe-start-tag.

    But the problems seems to be in that
    document.getElementById(‘Target_frame_ID_Name’).scrollHeight
    returns too big value if the previous page loaded in iframe was longer than the current. In other words the scrollHeight never gets smaller. In IE55SP2 it does.
    Also in the IE55SP2 I can scroll with the mouse scroll wheel on the iframe, but not with Firefox 1.0.3 or Mozilla 1.7.7! Thanks for reading thru all my rant.