Open Bug 1940465 Opened 1 month ago Updated 27 days ago

Consider having AddEventListener fire immediately for one-time events that have already happened

Categories

(Core :: DOM: Events, enhancement)

enhancement

Tracking

()

People

(Reporter: jesup, Unassigned)

Details

A cause of webcompat issues has been found to be one-time events that fire earlier in one browser (firefox) than another (Chrome). If a site adds the event handler after the one-time event happens, it will never fire. The example we've hit for sure is the page's load event, but there are a few other events that are similar, like domcontentloaded.

If we check on AddEventListener if we're adding a listener for a one-time-event, and that event has already occurred, we could fire the event immediately. In most cases, this is what the page actually wanted to happen, but because of timing it would get missed. It is possible that some pages might be broken by this, if they always were adding it after the event happens (like adding a domcontentloaded listener from the load event), and if making the call breaks something on the page (since it wouldn't have ever(?) been tested). This seems to be far less likely than sites accidentally adding races (both in firefox and chrome).

We should find a list of such one-time events, and if this works well on nightly, talk to google & standards groups about standardizing this behavior.

We should add an origin blocklist for this feature, so if a site does get broken, we can easily ship an intervention to fix them.

Given how easy it is to write code like:

window.addEventListener("load", doSomething);
if (document.readyState === "complete") {
  doSomething();
}

And / or dispatching events like window.dispatchEvent(new CustomEvent("load"))...

I really doubt this would be compatible generally. But maybe worth a try?

Also it might be very confusing for stuff to run synchronously under addEventListener...

Yeah, I'm rather sure that doing this would cause more problems that it would fix (I have personally written the exact code pattern Emilio mentions above where you check the readyState and either do something synchronously or async).

If you wanted to try a really hacky thing only for load /DOMContentLoaded events, I think you'd need to at least limit it only to cases where the readyState property hadn't been accessed. But I'm still rather sure that this would cause breakage.

(In reply to Emilio Cobos Álvarez (:emilio) from comment #2)

Given how easy it is to write code like:

Right, I suspect we would want to check at addEventListener time, whether the intended handler callback function has already been called or not, and only then call it if it hasn't, and the readystate implies they were "too late". But presuming that's not a big ask, there's always a lot of ifs here which could only have answered by trying it out.

(In reply to Thomas Wisniewski [:twisniewski] from comment #4)

Right, I suspect we would want to check at addEventListener time, whether the intended handler callback function has already been called or not, and only then call it if it hasn't, and the readystate implies they were "too late". But presuming that's not a big ask, there's always a lot of ifs here which could only have answered by trying it out.

But that doesn't work for code like comment 2 right?

We don't have to call it synchronously at that point, we could make an asynchronous call to it

(In reply to Emilio Cobos Álvarez (:emilio) from comment #5)

(In reply to Thomas Wisniewski [:twisniewski] from comment #4)

Right, I suspect we would want to check at addEventListener time, whether the intended handler callback function has already been called or not, and only then call it if it hasn't, and the readystate implies they were "too late". But presuming that's not a big ask, there's always a lot of ifs here which could only have answered by trying it out.

But that doesn't work for code like comment 2 right?

Presumably we could queue up the callback on the addEventListener line, with a special flag saying that when the time comes, we don't call the function if it was already called since then? (or un-queue it during the sync call in the if-statement if that's not too inefficient to process)? But yeah, this is probably going to take some iteration to figure out.

I would prefer to delay the https://html.spec.whatwg.org/multipage/parsing.html#stop-parsing steps (before step 2 for DOMContentLoaded, step 8 for load) over invoking listeners after the event has fired. But doing so for all sites is not great (and would likely regress perf scores), so maybe only on a per-origin basis to address web compat issues.

Otherwise, maybe there's something we can do to make the timing more interoperable?

I can' see how this could work. This would make the APIs behave rather oddly and also, there can be multiple event listeners for load/DOMContentLoaded etc. What if only one of them is set too late, would the other listeners get another event or what ?

You need to log in before you can comment on or make changes to this bug.