Bug 1598674 Comment 4 Edit History

Note: The actual edited comment in the bug view page will always show the original commenter’s name and original timestamp.

That live DOM viewer test basically has the following structure:

1) Create a Promise `p`.
2) Create an iframe element.
3) Set its load event to resolve `p`
4) Add a `then` handler to `p` that does `document.write()` followed by `document.close()` into the document in the iframe.
5) Insert the iframe in the document.
6) Check whether a second load event fires after the `document.close`.

There is a known behavior difference here between Firefox and the spec on the one hand and Chrome/Safari on the other hand.  Specifically, when the iframe is inserted into the document, Chrome/Safari fire the load event before the `appendChild` call returns, while Firefox, and the spec, fire it from a task asynchronously.

So what happens here in Firefox, and per spec, is that we start at https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-iframe-element:process-the-iframe-attributes and then go to https://html.spec.whatwg.org/multipage/iframe-embed-object.html#process-the-iframe-attributes the second case, and end up executing https://html.spec.whatwg.org/multipage/iframe-embed-object.html#iframe-load-event-steps async, from a task.  In those steps, we fire the load event, which has a listener that resolves a promise.  Since there is no script on the stack (because we are tunning async!), that promise's reaction microtasks are run right after the event listener returns, still under event dispatch.  That runs the function that does the `write`/`close()` bits.  Note that this is all happening under step 4 of "iframe load event steps", so the "iframe load in progress" flag that got set in step 3 is true.

Now when that flag is true at a point when an `open()` (explicit or implicit) happens, then https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#document-open-steps step 13 sets the "mute iframe load" and then https://html.spec.whatwg.org/multipage/iframe-embed-object.html#iframe-load-event-steps step 2 suppresses the load event from the `document.close()`.  So there is no second load event.

What happens in Chrome and Safari is that the iframe load event fires sync, while the script that called `appendChild` is on the stack.  There is therefore no microtask checkpoint during that script, and in particular the promise that is resolved in the load event does not have its reactions run until that script completes, at which point the "iframe load in progress" flag is _not_ set anymore.  So in those browsers the `open` happens without that flag set, does not set "mute iframe load", and you get a second load event.

See also discussion in https://github.com/whatwg/html/issues/4965

The simple test fix here is to resolve the promise explicitly async from the load event, so we don't end up dealing with this "iframe load in progress" stuff.
That live DOM viewer test basically has the following structure:

1) Create a Promise `p`.
2) Create an iframe element.
3) Set its load event to resolve `p`
4) Add a `then` handler to `p` that does `document.write()` followed by `document.close()` into the document in the iframe.
5) Insert the iframe in the document.
6) Check whether a second load event fires after the `document.close`.

There is a known behavior difference here between Firefox and the spec on the one hand and Chrome/Safari on the other hand.  Specifically, when the iframe is inserted into the document, Chrome/Safari fire the load event before the `appendChild` call returns, while Firefox, and the spec, fire it from a task asynchronously.

So what happens here in Firefox, and per spec, is that we start at https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-iframe-element:process-the-iframe-attributes and then go to https://html.spec.whatwg.org/multipage/iframe-embed-object.html#process-the-iframe-attributes the second case, and end up executing https://html.spec.whatwg.org/multipage/iframe-embed-object.html#iframe-load-event-steps async, from a task.  In those steps, we fire the load event, which has a listener that resolves a promise.  Since there is no script on the stack before the listener's code is entered (because we are running async!), that promise's reaction microtasks are run right after the event listener returns, still under event dispatch.  That runs the function that does the `write`/`close()` bits.  Note that this is all happening under step 4 of "iframe load event steps", so the "iframe load in progress" flag that got set in step 3 is true.

Now when that flag is true at a point when an `open()` (explicit or implicit) happens, then https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#document-open-steps step 13 sets the "mute iframe load" and then https://html.spec.whatwg.org/multipage/iframe-embed-object.html#iframe-load-event-steps step 2 suppresses the load event from the `document.close()`.  So there is no second load event.

What happens in Chrome and Safari is that the iframe load event fires sync, while the script that called `appendChild` is on the stack.  There is therefore no microtask checkpoint during that even firing, and in particular the promise that is resolved in the load event listener does not have its reactions run until the script that called `appendChild` completes, at which point the "iframe load in progress" flag is _not_ set anymore.  So in those browsers the `open` happens without that flag set, does not set "mute iframe load", and you get a second load event.

See also discussion in https://github.com/whatwg/html/issues/4965

The simple test fix here is to resolve the promise explicitly async from the load event, so we don't end up dealing with this "iframe load in progress" stuff.

Back to Bug 1598674 Comment 4