Bug 235322 - Adding iframe flushes microtasks synchronously with dirty stack
Summary: Adding iframe flushes microtasks synchronously with dirty stack
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: DOM (show other bugs)
Version: Safari 15
Hardware: All All
: P2 Normal
Assignee: Ryosuke Niwa
URL:
Keywords: InRadar
: 243488 (view as bug list)
Depends on:
Blocks:
 
Reported: 2022-01-18 10:23 PST by Dan Abramov
Modified: 2023-01-24 11:40 PST (History)
14 users (show)

See Also:


Attachments
repro (400 bytes, text/html)
2022-01-18 10:23 PST, Dan Abramov
no flags Details
WIP (939 bytes, patch)
2022-06-28 23:53 PDT, Ryosuke Niwa
no flags Details | Formatted Diff | Diff
WIP2 (2.92 KB, patch)
2022-06-29 01:13 PDT, Ryosuke Niwa
no flags Details | Formatted Diff | Diff
Patch (8.15 KB, patch)
2022-06-29 11:19 PDT, Ryosuke Niwa
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Dan Abramov 2022-01-18 10:23:06 PST
Created attachment 449401 [details]
repro

Microtasks are supposed to run on a clean stack. However, Safari flushes microtasks synchronously if you append an iframe to the page. This breaks expectations for libraries like React/Preact, and possibly others:

- https://github.com/facebook/react/issues/22459
- https://github.com/preactjs/preact/issues/2814


Here is a simple repro:

```
queueMicrotask(() => {
  console.log("--- in microtask ---");
});
console.log("will add iframe");
const iframe = document.createElement("iframe");
iframe.src = "localhost";
document.body.appendChild(iframe);
console.log("did add iframe");
```

You need to run it with http server to repro. It doesn't repro from file://.

Expected output:

```
will add iframe
did add iframe
--- in microtask ---
```

Safari output:

```
will add iframe
--- in microtask ---
did add iframe
```
Comment 1 Jason Miller 2022-01-18 13:20:46 PST
I spent a bit of time checking various forms of this issue:
https://jsfiddle.net/developit/u6xztc93/

I was able to narrow the issue down:

- microtasks are only flushed in the realm of the frame's ownerDocument.
- flush is *not* triggered for sandboxed iframes.
- flush *is* triggered for cross-origin iframes.
    (presumably because it's not known to be cross-origin at append time)
- flush *is* triggered for iframes within <svg><foreignObject>.
- other elements (svg/img/video/object/embed) do not trigger flush.
Comment 2 Radar WebKit Bug Importer 2022-01-25 10:23:43 PST
<rdar://problem/88031232>
Comment 3 Ryosuke Niwa 2022-06-28 22:50:38 PDT
I don't think this is a regression. The issue really stems from the fact we're still firing DOMContentLoaded and load events synchronously in Document::finishedParsing(). 

Since this code gets to run when inserting an iframe as a part of creating the initial empty document, we end up performing the microtask checkpoint.

Making DOMContentLoaded and load events fully asynchronous is a major undertaking but we can workaround this specific issue by detecting that's what's about to happen and avoid performing microtask checkpoint.
Comment 4 Ryosuke Niwa 2022-06-28 23:53:55 PDT
Created attachment 460532 [details]
WIP

We can detect the case where we're creating initial empty document like this.
But this doesn't work when we're implicitly navigating to about:blank (blank iframe).
Comment 5 Ryosuke Niwa 2022-06-29 01:11:38 PDT
Yikes. This must be a regression from https://trac.webkit.org/r234944.
Comment 6 Ryosuke Niwa 2022-06-29 01:13:19 PDT
Created attachment 460534 [details]
WIP2
Comment 7 Ryosuke Niwa 2022-06-29 11:07:49 PDT
Pull request: https://github.com/WebKit/WebKit/pull/1907
Comment 8 Ryosuke Niwa 2022-06-29 11:19:13 PDT
Created attachment 460549 [details]
Patch
Comment 9 Ryosuke Niwa 2022-06-29 11:23:39 PDT
Pull request: https://github.com/WebKit/WebKit/pull/1909
Comment 10 EWS 2022-06-30 14:40:46 PDT
Committed 252015@main (ea74655e102a): <https://commits.webkit.org/252015@main>

Reviewed commits have been landed. Closing PR #1909 and removing active labels.
Comment 11 Ryosuke Niwa 2022-08-05 17:40:22 PDT
*** Bug 243488 has been marked as a duplicate of this bug. ***
Comment 12 Matt d'Entremont 2023-01-24 09:47:57 PST
The commit for the fix doesn't seem to be part of any Safari-specific git tags, but it is in Safari branches safari-7614.1.20-branch all the way up to safari-7615.1.9-branch.

I see it mentioned in the Safari tech preview 150 release notes, but not in any of the mainline release notes.

Is this released to the general public yet?
Comment 13 Ryosuke Niwa 2023-01-24 11:40:43 PST
(In reply to Matt d'Entremont from comment #12)
> The commit for the fix doesn't seem to be part of any Safari-specific git
> tags, but it is in Safari branches safari-7614.1.20-branch all the way up to
> safari-7615.1.9-branch.
> 
> I see it mentioned in the Safari tech preview 150 release notes, but not in
> any of the mainline release notes.
> 
> Is this released to the general public yet?

It should be in Safari 16.