Iframes / XMLHTTPRequest bug in Firefox

Published June 27th, 2005

For the last month or two I’ve been sweating away developing a new, all-singing all-dancing version of the CMS for the project we’re working on. I’ve been making extensive use of XMLHTTPRequest (Ajax, if you must) techniques to streamline workflow. And it’s all been working pretty well so far.

However, because (as far as I’m aware) XMLHTTPRequest can’t handle file uploads, I’ve had to be a bit more creative in coming up with a seamless way to allow files to be uploaded (ie. without a page refresh). After some fiddling about I resorted to a method that uses a dynamically-created, hidden iframe as the target for the upload form. It goes something like this:

  • User chooses file and clicks submit
  • Iframe is dynamically created and added to the page (using appendChild())
  • Form is submitted through iframe
  • Iframe executes callback function in parent document
  • Iframe is removed (using removeChild())

And it works pretty well. But I found an odd quirk with Firefox (I’m using version 1.0.4, which is the current stable version at the time of writing). If an XMLHTTPRequest is executed, by a function that’s initiated by the iframe, after removing the iframe, then Firefox throws the following exception:

Error: uncaught exception: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsIXMLHttpRequest.open]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: [File name] :: [Function name] :: line [ line number ]" data: no]

Please ignore the fact that I should really be catching all exceptions :)

At first I thought it must be security thing (allowing an iframe to call XMLHTTPRequests in its parent could, I suppose, be considered a potential security hole), but if you try the same thing without destroying the iframe then it works just fine. Which leads me to think it must be a *gasp* bug.

I checked bugzilla and found what sounds like the same problem in different circumstances so added my own findings and this reduced test case (source). I hate submitting to Bugzilla because I always seem to get shouted at for some reason, but maybe my submission will be enough to help it get fixed — although it’s been there for nearly a year now so I’m not holding my breath.

Solutions? Well I tried various approaches but ended up with a rather ugly fix: the callback function itself (in the parent document) just stores the data that’s supplied by the iframe. In the meantime, a function is repeatedly checking (using setTimeout()) whether any data has been returned and acts accordingly if it has. That way, it’s the parent document that initiates any subsequent XMLHTTPRequest calls, rather than a function that’s been called by the iframe. Not ideal, but it does the job.

Get a Trackback link

37 Comments

  1. Jason on August 3, 2005

    I am glad I am not the only one who has run into this problem. I would be interested to see how your callback function works.

  2. Stickman on August 4, 2005

    I’m afraid I can’t post the code because a) it’s all part of an extensive and rather complicated codebase and wouldn’t really make sense on its own and b) even if I could, I’d probably get in trouble with my employer for doing so.

    But I’m happy to explain the technique in a bit more detail (I’ve changed/omitted some of the details but the basic premise stands):

    First, I create a callback function in the parent document — let’s call it storeData() and say it is passed one argument, callback_data — which will be called by the iframe and passed any data that’s returned.

    At the same time, I create a global variable called window.stored_data and set it to false. This is where storeData() will put any data that’s returned by the iframe (ie. callback_data).

    Finally I use setTimeout() to call another function — let’s call it checkData() — after a tenth of a second (or some other interval) to check the value of window.stored_data.

    If window.stored_data is still false, checkData() uses setTimeout() to call itself again after your given time period (it’s a good idea to build in a limit to how many times checkData() calls itself before returning a ‘request timed out’ error).

    If window.stored_data is not false, the the page can do whatever it needs to with the returned data. Just make sure that the iframe never sends ‘false’ as a return value. :)

    As I said it’s not an elegant solution but it does work. I hope this helps.

  3. Debbie on August 26, 2005

    Well I too am having similar problems but not because I’m using an iframe. I’m opening a popup window and it call a javascript xmlhttprequest call from it’s parent window. All works fine until i close the popup window. Then the next xmlhttprequest call I make for the original “parent” window fails with the same error.

    My work around was to create a XMLHttpRequest new on every call to the script.

    I’m new to all of this so I hope my comments make sense.

    d

  4. nate beaty on September 4, 2005

    I’m about to attempt something very similar — and I was wondering why you don’t just leave the Iframe on the page (check if it exists on each call, create it if not), and reuse it when needed. Why is it necessary to removeChild() ?

  5. Stickman on September 6, 2005

    For neatness, more than anything — I like the idea that the iframe is ‘tidied up’ after use, and not left lying around in case it’s needed again. But that’s just me, off the top of my head I can’t think of any technical reason.

  6. Philip Snyder on September 14, 2005

    I encountered this same issue with popups as well. However, I’ve tried creating a new XMLHTTPRequest after the last popup, before every popup… you name it, I tried it. I really didn’t want to use iframes for my remote scripting due to history issues, but until this BUG (yes, I blatantly call it a bug and dare anyone to challenge me) I’m going to be forced to. Sucks if you ask me. I was all excited because it was going to be easy and work fantastically. My other option is to recode all the popups into DHTML windows — but this project simply does not have the time. -sigh-

    Thanks for your help guys. I thought it was coding error and would have wasted another day on this.

  7. sidspencer on October 12, 2005

    gecko seems to get confused as to how to clean up related documents. i’m working with a file upload situation which is done very similarly to your initial scenario, with an iframe containing a form and a file input which is placed inside of a document containing a larger form so that the upload can be handled independently. we kept our iframe script-free, having the main document call submit() on the iframe’s form, then hide the iframe and show the progress bar div, and update the progress bar by making xmlhttp requests to the upload servlet until it returned 100%. we had no problems with this.

    however, we had a very odd problem such that when we moved away from the main form (unfortunately done by setting new innerHTML on our content div… none of the damn web framework has my blessing), all non-local javascript on the next page would be broken. i investigated using venkman, and found that the call from the “ajax” controller object which set content.innerHTML = newHTML was never returning. as this didn’t happen on any other page, only on the page which contained an iframe, i also decided to clean up the iframe. that’s when the error message changed from the very opaque nsIDOMLocation NS_ERROR_FAILURE from <unknown> with data:no, and started failing only on XMLHttpRequest.open().

    i think this is probably a reference counting problem inside of the DOM which is being obscured. if i find a good workaround, i’ll submit it as another comment to you. i’d like to look at the gecko source and see if my suspicion can lead me anywhere, but i doubt i’ll have time.

    thank you for writing this article and pointing me to the exact bug report.

  8. sidspencer on October 12, 2005

    i turned out to be mistaken — the iframe was totally using a callback. once i refactored it so that the iframe never called any javascript, everything started working well.

  9. whynot on October 22, 2005

    Stickman, not sure if you’ve found a workaround, but here it goes my suggestion… maybe you could dynamically create and send a simple FORM within the hidden frame rather than using XMLHTTPRequests.

  10. Stickman on October 22, 2005

    Iframes are an alternative — in fact, I have my system working so that iframes are used as a backup wher XMLHTTPRequest is not available. But they have their problems too, so I prefer to use them only when there’s no choice.

  11. Flyboy on October 24, 2005

    damn, it seems that every answer to my questions is here on this site :)

    now only getting it to work, that will be the hard part :p

  12. Itzik Ziv on October 29, 2005

    Here is a workaround:
    Call the httpRequest in the parent frame thru a timer call, and not directly.
    something like:
    function delayhttpRequest (url) {
    setTimeout(“httpRequest.open(‘GET’, url, 1)”);
    }
    it will make the method invocation from the parent frame
    and not from the child iframe.

    enjoy
    Itzik

  13. Execute on November 1, 2005

    yea i tried setting the POST contentType in my AJAX script to “multipart/form” or whatever instead of “aplication/www-x-urlencoded” or whatever… but that didnt work so i have to just use an iframe. But i dont believe you really need to do this.
    Why not just make an invisible iframe, and then when the form is submitted, it targets the iframe, and the iframe is enabled thru Javascript to be visible, thus you have a iframe , which does not look like an iframe, it looks like AJAX, but its actually displaying results in an iframe?
    Why do we need it hidden, and make it so complicated that it should be automatically created and then destroyed and then data is stored, and all this extra complication? why not just echo the results in an iframe ? I mean is there a bad thing about iframes i dont know about?

  14. Stickman on November 1, 2005

    It’s more to do with transparency/ease of use than anything. I wrote my XMLHTTPRequest and Iframe classes to be interchangeable (back when I was aiming for total cross-browser compatibility). It was more effort but now I’ve overcome the various problems, it works really well.

    I’m not advocating this as an approach — do what works for you! I posted this thread because I found a problem that I thought others might also come across.

  15. Execute on November 1, 2005

    ive tested AJAX in almost all browsers, opera, firefox, IE, etc i havent tested in mac but still, i dont think XMLhttprequest is incompatible with any of the browsers really… except maybe old versions… but thats like 1% of the web.. :/

  16. Stickman on November 1, 2005

    Safari was the biggest problem. I don’t remember version numbers (I’m not a Mac user) but if you don’t have a particular version of OSX or above, the version of Safari that’s available to you doesn’t support XMLHTTPRequest at all.

    Opera has only fully supported XMLHTTPRequest in the most recent versions. Back when I was writing this stuff, support was only partial (basically, GET requests only).

    I put in quite a few hours trying to enable support for these browsers, but eventually gave in when it became clear that the number of obstacles was just too great. However, by that time I had a fully-working iframe request class, and having put in the dev time I was loath just to chuck it out. :)

  17. Execute on November 1, 2005

    You just cant make everyone happy. But obviously you know the majority of the web uses IE and FireFox… Netscape, Safari, Opera, konquqeror or whatever, those are minorities; Opera is a good browser so i would support that, but the others are so unknown, well safari for mac is important but I really dont think there are that many mac users either. I would say that no one really uses netscape, i would be surprised if anyone still uses it. Konquerer i don’t believe there is over a million users of that. I’ve seen charts of this, and it seems the top browsers are :
    Internet Explorer (85% of the web) a lot of users
    FireFox( 6% of the web) 110 mil users
    Safari (2.2% of the web) prolly around 15 mil
    Opera (2%) around 12 mil
    others(4.2%) (netscape, konq bla bla)

    Thats approximately what it is. Unless you’re trying to run a website, for a huge corporation or a support center or something that users really really need quick access… i dont think u need to be compatible to some of the lower browsers. I usually test for most of em, but i cant test for safari… :/ so that sux… I need more of a website that tells me the specific detailed descriptions perhaps javascript descriptions of each browser, so i know which browser doesnt support what… I think the benefit of using advanced features vs the benefit of being cross browser might prove better… Well for example, if you’re trying to serve a large population of online users for lets say a website like download.com or cnn.com then you would want to be very compatible, but if you’re like me who makes a website for a fairly large amount of users, but mostly you’re a service offering website, then AJAX would still be acceptable.

  18. Execute on November 1, 2005

    Oh and btw… i have been developing my own AJAX CMS system, i have started a little, but there is soooooo much involved in it, and its very complicated, like i need a lot of planning to do it right. So far i have the layout, filemanager system, and upload, and editting pages, but thats about it. I looked at other CMS systems and they seem to have a LOT more functions :X… But hopefully making it AJAX will interest users to use mine more. or that sort of thing. I hope sometime maybe you’ll post a demo of yours. Good luck :)

  19. Stickman on November 1, 2005

    I can’t really talk about the detail of what I’m working on, but when I say it’s a massive project I’m not exaggerating. The CMS I’m writing is totally proprietary — it’ll never be available to the general public. Which is a shame, because some bits of it I’m really quite proud of and it’ll only ever be seen by a relatively small number of people. Ah well. I’m learning a lot anyway, and some of it is the sort of thing I can post here to help other people out. :)

    Best of luck with your own project!

  20. nukq on November 13, 2005

    I am having similar problems. i create a iframe to get some response from server( re.php ). and call a parent’s function from the iframe.
    the function do something XmlHttpRequest…

    re.php:

    …. eval(”)

    change to:

    …. top.setTimeout(”,200);

    ps. $callback is something like : top.B_HandleAction(\’getDetail\’,34)

    now, it works very fine!!

    thank all you!

  21. nukq on November 13, 2005

    oh… no…

    eval(‘<=$callback>’);

  22. Execute on November 20, 2005

    I understand how to do all your steps btw stickman, but im still confused as to how to do the “callback” function or whatever that is. Right now im thinkin about making a little div inside the iframe and then reading the response there and remaking it in the parent document then deleting the iframe.

    otherwise i dont know how to make a “callback” function or however you call it.

  23. Russ on December 18, 2005

    just put the iframe in a hidden table at the end of the page with its position property set to absolute, (centering it on the page for convenience by setting the top/left properties as well). simply toggle the css display property from hidden to block, and you can show it when you need it and hide it when you don’t. that way you avoid the bug and as well as all those ugly set/clearInterval calls.

  24. Russ on December 18, 2005

    one more note…

    you also have to set the iframe’s display property seperately, since it won’t inherit it from its containing table/cell for some reason.

  25. Mustafa on March 17, 2006

    Hi,

    I was using a hidden Iframe to interact with my database without making the user wait for the time consuming interaction to complete. The hidden Iframe loads a html file which auto-submits and calls another database update file (JSP).

    This not the ‘AJAX’ way, but rather a primitive old fashioned “remote scripting” thingy.
    All works fine with IE and opera. But in firefox their are issues.

    In firefox the file in an hidden ifarme will render, but the resultant file after the form submit does not !
    Quite strange behaviour.

  26. Ambrose Little on September 12, 2006

    I just ran into this today. Your blog has the only solution I ran across. Setting window.top.setTimeout worked for me. Muchas gracias!

  27. Will Ryan on December 8, 2006

    I wasted so much time trying to make setTimeout work within an iframe, and I finally found the solution here…. so obscure!!!! top.setTimeout did the trick (whereas before, nothing was happenning!).

    Thanks.

  28. RghSx on May 8, 2007

    We had this same problem – we have a multiple upload file script that when submitted went to a hidden iframe that was generated by javascript. In the script that processes the file uploads we make some js callbacks ex(parent.document.getElementById(‘some_element’).value=”something”), including one call that performs some AJAX operations.

    We solved this by removing the iframe from being generated by the javascript to be just a hidden frame on the main page. Apparently, there seemed to be a security issue with javascript generated iframes in firefox not allowing AJAX calls. Suffice it to say that all was fixed by moving the iframe out of the javascript.

  29. Nathan on May 17, 2007

    Hey guys,
    Don’t have time to read through everything but I did want to through this in. I am haveing the exact same issue. It sounds like the issue is that the XMLHTTP request is essential going out of scope ( or similar ) when the iframe is removed from the DOM. So when the request is returned from the server things are messed up. I have not tested this yet but I am working on a request manager that basically adds each new request to a queue and then executes them sequentially ( it is a lot more complicated but this is basically what happens ) I suspect that by passing the request data into the queue in the parent document the link between iframe and request is broken as the request manager creates the request not the iframe encapsulated code.

    I will try to test this soon, if anyone beats me too it please post the results.

  30. Alan on December 8, 2007

    Yes Iframes can be a real pain when it comes to Firefox so on http://www.WebSiteTips.eu i resorted to Ifreames for IE and a frameset for firefox.

    You will thin Microsoft would had sort the file upload stuff out by now and have a the option of using Ajax or maybe they do by now but i know few of the Ajax controls do quite what they say on the box.

  31. Phzzy on December 27, 2007

    you can use this:

    function callback() {
    setTimeout(“delay_ajax()” , 10);
    }

    function delay_ajax() {
    // send XMLHTTPRequest
    ……
    }

    ~~

    Phzzy

  32. Draders on December 27, 2007

    I am making an ajax cms for my company and set up my upload api similar to yours, I fixed the issue by adding a small delay before removing the iframe after its page has loaded.

    setTimeout(function() { sickle.ajax._removeFrame(frame); }, 1);

    _removeFrame : function(id) {
    var tmp = [];
    for (key in sickle.ajax.frames) {
    if (key == id) {
    var p = sickle.ajax.frames[key].parentNode;
    p.removeChild(sickle.ajax.frames[key]);
    }
    else {
    tmp[key] = sickle.ajax.frames[key];
    }
    }
    sickle.ajax.frames = tmp;
    }

  33. ilango on August 8, 2008

    I created web service using Rest.It works fine in a single machine.but access from different machines XMLHttpRequest.open(“GET”,”https://192.168.1.2/”,”true”) leave a exception “Access to restricted URI denied” security error.How to overcome it.please give the solution.

  34. Stickman on August 11, 2008

    ilango: XMLHTTPRequest does not work between different domains. The browser will report any attempt to request a file from another domain using XMLHTTPRequest as a security violation.

  35. Jakov on October 23, 2008

    Hi Stickman, it’s passed 3 years ago when you post this blog. Can you post your code here???

    My English is very bad, and I can’t reed this at all.. :(

    I have same problem like was yours… from I must call XMLHttpRequest but you know what is happening… Thanks!

  36. trying on January 24, 2009

    dear all,
    how do i save the text written in an iframe to my servlet,for further b sent to the database…..i m a beginer in servlets,please help me with some example and syntax

    thanks

  37. Tyron on September 27, 2009

    Thank you very much for your entry. I had a verrry similar problem while making my own multi-file-upload for my own CMS as well ^^
    But blog entry ended my almost 2 hour search for a solution.

Leave a comment

Comment Policy: First time comments are moderated. Please be patient.

OpenID

Anonymous