-
Notifications
You must be signed in to change notification settings - Fork 4.6k
Iframe: avoid asset parsing & fix script localisation #52405
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
3 changes: 3 additions & 0 deletions
3
packages/e2e-tests/plugins/iframed-enqueue-block-assets/script.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| window.addEventListener( 'load', () => { | ||
| document.body.dataset.iframedEnqueueBlockAssetsL10n = window.iframedEnqueueBlockAssetsL10n.test; | ||
| } ); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ellatrix, can you provide some additional details about this hack? Why is it needed, and what does it solve?
It looks like some internal changes in React 19 affect it: #61521 (comment).
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What this does: the
stylesandscriptsare HTML literal values that contain styles and scripts to load. Their source is the__unstableResolvedAssetssetting: a HTML chunk generated by the PHP_wp_get_iframed_editor_assetsfunction.The initial document in the
iframeis thishtmlthat does the preloading. It:_load()call)bodyelement is removed by thescriptinside it.At the same time, React will render a portal markup inside this
iframeonce the_loadcallback is called. This is the code that does this:The rendering of the portal is triggered by the
_loadcall. That means that the portal is rendered, and itsbodyis rendered, while the scripts and styles in the original document are still loading.Whether this all works depends on the subtle details of what exactly React does when rendering the portal. The original content of the iframe is:
The portal is rendered into the
<html>element, that's whatiframeDocument.documentElementpoints to. What happens to the existing<head>and<body>markup? I don't know. Usually the elements into which portals are rendered are supposed to be empty.Script localization is done with inline script tags. But if you look at the original code before this patch, namely the
loadScriptfunction, it can handle only<script src="...">tags that load from a URL. Now the HTML chunks with thescripttags are directly pasted as strings into thehtmlcontent. And inline script tags are working.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In #61521 @tyxla changed the portal-rendering condition to:
adding a check if
.bodyis there. But this is problematic. Thenode._load()call which triggerssetState( iframeDocument )happens in the iframe document'shead. When that script is executed, however, thebodyelement doesn't exist yet. The portal is not rendered. Later, when thebodyelement is created, nobody tells React about that, there is no state update. The portal remains not rendered. Until some other unrelated state update triggers the component rerender and the.bodycheck finally succeeds.Try saving this into a
.htmlfile and open it in a browser:The first
console.logwill lognull, the second one will log abodyelement.One very important thing about this is that between the
._load()call and the<body>element the HTML contains several scripts and styles loaded over the network. There is significant delay between them.@Mamaduka created a Codesanbox toy example that doesn't have this property. There is nothing async there between the
_load()and the<body>. TheiframeDocument?.bodycode will reliably see the already existingbody.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just verified on a toy app how React 18 and React 19 differ when rendering portals.
Load an
iframewith this HTML assrcin a blob URL:and then render a portal into that
iframe'shtmlelement:React 18 creates two
bodyelements, one withoriginalcontent, other withportalcontent.React 19 removes the
originalelement and replaces it withbodywithportalcontent.The
headelement is left intact, themetaand thescripttag are still there. In both React 18 and 19.So far this seems that React 19 fixed a bug with the double
bodyelement. And that we no longer need the self-destructingbodyelement.I still don't understand how this change could break some behavior and e2e tests. I'll continue looking into this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, @jsnajdr!
Btw, I also noted that difference in #61521 (comment). Sorry, I should've highlighted it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's necessary to delete the body element, not just to avoid having two body elements, but also to prevent other scripts from attaching event handlers to the wrong body element.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW this wasn't breaking E2E tests, it was literally breaking the editor. I did change it a few months ago when testing with one of React 19 betas, so I'll need to come back to it and see if it's still necessary, and if yes, if there is a better solution. Hoping to come back to this early next year.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've reverted these changes in #61521 and retried this.
I think we still need to stop removing the body element, since in React 19 it will now ensure there's always one and only one
bodytag. I'd love to learn more about the script event handler issue you're talking about @ellatrix - how can we repro this?The only other change from the original code is that we're moving the frame loading just a bit later, after the other scripts in the iframe.