Skip to content

load event handling for iframes with no src may not be web compatible #4965

Open
@bzbarsky

Description

@bzbarsky

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:

  1. 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.
  2. The event listener is added.
  3. The load in the iframe starts.
  4. 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).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions