[whatwg] Load events fired during onload handlers

Ian Hickson ian at hixie.ch
Thu Nov 29 21:14:25 PST 2012

On Mon, 30 Jul 2012, James Graham wrote:
> There seems to be general agreement (amongst browsers, not yet the spec) 
> that if a document does something that causes a new load event from 
> within an onload handler (document.open/document.close) the second load 
> event is not dispatched. This also applies to the load event on iframe 
> elements if an event handler in the iframe would synchronously cause a 
> second load event to fire.
> There is not agreement about what happens where there are multiple 
> frames e.g. if a load event handler on iframe element A would cause a 
> load event in iframe B, should the handler on B fire. Gecko says yes, 
> WebKit no. There is a slightly rubbish demo at [1].
> [1] http://software.hixie.ch/utilities/js/live-dom-viewer/saved/1686

On Tue, 31 Jul 2012, Boris Zbarsky wrote:
> Gecko fires the load event on the <iframe> as part of the default action 
> of the load event on the window inside that iframe.  I think the spec 
> might actually call for it to fire async instead, though I haven't 
> tested what other UAs do there.

Per spec it's effectively async. More importantly, though, per spec the 
'load' event on the <iframe> is delayed by some stuff that the 'load' 
event _in_ the <iframe> isn't delayed by, e.g. readyState changes, 
pageshow is fired, and delayed printing steps are executed, between them.

> The code for firing the load event looks somewhat like this:
>     if (!mEODForCurrentDocument) {
>         mIsExecutingOnLoadHandler = true;
>         // Fire onload here
>         mIsExecutingOnLoadHandler = false;
>         mEODForCurrentDocument = true;
>     }
> and the handling for document.open() sets mEODForCurrentDocument to 
> false. Now what happens is we enter the above code for the normal load.  
> We fire the onload handler, which does the open()/write()/close() thing.  
> That tries to set mEODForCurrentDocument to false, but of course it's 
> already false.  When close() happens, onload does NOT fire sync from the 
> close() call, because there are still outstanding async tasks on the new 
> document that block onload. So we unwind the stack to the code above, 
> and set mEODForCurrentDocument to true, and block any further firing of 
> onload for this document until another open() call happens.
> Fundamentally, that looks like a bug in the handling of 
> mEODForCurrentDocument, really.
> So if either that bug were fixed or item #1 above were changed, I think 
> you'd get two load events here in Gecko right now.

I haven't changed the spec to match Gecko here, since you seem open to 
changing Gecko. :-)

On Mon, 30 Jul 2012, James Graham wrote:
> On 07/30/2012 05:44 PM, Boris Zbarsky wrote:
> > On 7/30/12 11:10 AM, James Graham wrote:
> > > I don't think I have a strong opinion about what should happen here, 
> > > but the Gecko behaviour could be easier to implement, and the WebKit 
> > > behaviour slightly safer (presumably the point of this anomaly is to 
> > > prevent infinite loops in load event handers).
> > 
> > In Gecko's case, the only thing like that I know of is that onload 
> > fires synchronously in Gecko in some cases, I believe. So we had to 
> > put in some sort of recursion guard to prevent firing onload on a 
> > parent in the middle of a child firing onload or something like that.  
> > See <https://bugzilla.mozilla.org/show_bug.cgi?id=330089>.  Per spec, 
> > onload is always async, so this wouldn't be a concern.
> Yeah, but as far as I can tell all browsers block (same document) load 
> events that happen from inside onload [1], so I *guess* at some point in 
> the past a site got into an infinite loop by trying to use document.open 
> from inside onload.
> [1] https://www.w3.org/Bugs/Public/show_bug.cgi?id=17231

I wrote a similar test to the one you put in the bug:


It shows the onload handler actually getting called again in Gecko.

The interesting thing about the test in the bug is that it's actually the 
<iframe>'s onload that's not being called again, no page onload is being 
tested in that test. So for that test, the solution I suggested in comment 
10 in that bug is worthless.

Also interesting is that if you check what's actually going on, you'll 
find that in Opera and Gecko (and per spec) the iframe's onload is run 
asynchronously, whereas in WebKit it's synchronous.

That it's asynchronous means that what we need to do is:

 * in the iframe code that fires 'load', annotate the iframe's document so 
   that it knows we're in a 'load' event handler,
 * in the document.open() code, set a flag to true if the aforementioned
   annotation is set, and false otherwise,
 * in the iframe code that fires 'load', don't fire 'load' if the
   aforementioned flag is set.

Sibling frames:


Gecko and Opera go on to infinity there; WebKit blocks, but being 
synchronous means that the algorithm above would block this case, so that 
makes sense.

I've put the above suggestion in the bug, if nobody objects to it then 
I'll spec that.

On Thu, 2 Aug 2012, Henri Sivonen wrote:
> For what it's worth, I think the weirdness described in this thread is a 
> good reason not to try to make DOMContentLoaded consistent with the load 
> event for the sake of consistency. For one thing, the code that manages 
> the weirdness of the load event lives in a different place compared to 
> the code that fires DOMContentLoaded.

Per spec they're about as consistent with each other as they can be, 
currently. (That's not to say that they're correctly specced; indeed 
there's an outstanding bug about making the "abort" steps work like the 
"the end" steps.)

Ian Hickson               U+1047E                )\._.,--....,'``.    fL
http://ln.hixie.ch/       U+263A                /,   _.. \   _\  ;`._ ,.
Things that are impossible just take longer.   `._.-(,_..'--(,_..'`-.;.'

More information about the whatwg mailing list