Consider this testcase:
var iframe = document.createElement('iframe');
document.body.appendChild(iframe);
var fn = () => {
iframe.remove();
console.log('error');
};
iframe.addEventListener('load', fn);
iframe.src = "https://www.seattlesymphony.org/~/media/files/notes/schubert-sonata-21-b-flat.pdf";
(where you can use any URL that has Content-Disposition: attachment for the src). Per spec what should happen here is this:
- During the
appendChild call https://html.spec.whatwg.org/multipage/iframe-embed-object.html#process-the-iframe-attributes runs and does the "Queue a task to run the iframe load event steps." bit.
- The event listener is added.
- The load in the iframe starts.
- The queued task from step 1 runs, fires the load event, the iframe is removed and "error" is logged.
What happens in browsers instead is:
- Chrome and Safari: The load event fires synchronously under the
appendChild call, as far as I can tell, so that the listener is added after the event has fired and hence the frame is never removed and "error" is never logged. The navigation does not fire a load event, because it has Content-Disposition: attachment and hence is not loaded in the iframe.
- Firefox: The insertion starts an async
about:blank load which would fire a load event async when it completes. The start of a new navigation cancels that load, so there is no load event fired at all. The frame is never removed and "error" is never logged. The navigation to the PDF does not fire a load event, because it has Content-Disposition: attachment and hence is not loaded in the iframe.
I don't have a good proposal for what the spec should say here... Currently the spec-compliant way for a page to do what the above script is trying to do is something like:
var iframe = document.createElement('iframe');
iframe.addEventListener("load", () => {
var fn = () => {
iframe.remove();
console.log('error');
};
iframe.addEventListener('load', fn);
iframe.src = "https://www.seattlesymphony.org/~/media/files/notes/schubert-sonata-21-b-flat.pdf";
}, { once: true });
document.body.appendChild(iframe);
which is rather annoying from an authoring perspective and hard for authors to think of.
That said, I expect that parser-triggered insertions likely do need the async load event thing in some way, though this testcase:
<iframe onload="console.log('sync-loaded')"></iframe>
<script>
document.querySelector("iframe").onload = () => { console.log('async-loaded'); }
</script>
shows "sync-loaded" in Chrome and Safari (and "async-loaded" in Firefox; per spec behavior could be either way depending on whether there's a packet boundary between the <iframe> and the <script>, afaict).
Consider this testcase:
(where you can use any URL that has
Content-Disposition: attachmentfor the src). Per spec what should happen here is this:appendChildcall https://html.spec.whatwg.org/multipage/iframe-embed-object.html#process-the-iframe-attributes runs and does the "Queue a task to run the iframe load event steps." bit.What happens in browsers instead is:
appendChildcall, as far as I can tell, so that the listener is added after the event has fired and hence the frame is never removed and "error" is never logged. The navigation does not fire a load event, because it hasContent-Disposition: attachmentand hence is not loaded in the iframe.about:blankload which would fire a load event async when it completes. The start of a new navigation cancels that load, so there is no load event fired at all. The frame is never removed and "error" is never logged. The navigation to the PDF does not fire a load event, because it hasContent-Disposition: attachmentand hence is not loaded in the iframe.I don't have a good proposal for what the spec should say here... Currently the spec-compliant way for a page to do what the above script is trying to do is something like:
which is rather annoying from an authoring perspective and hard for authors to think of.
That said, I expect that parser-triggered insertions likely do need the async load event thing in some way, though this testcase:
shows "sync-loaded" in Chrome and Safari (and "async-loaded" in Firefox; per spec behavior could be either way depending on whether there's a packet boundary between the
<iframe>and the<script>, afaict).