Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
58acf8c
Explore making all iframes controlled
adamziel Nov 20, 2025
76af9ec
Generalized support for controlling all the iframes
adamziel Nov 20, 2025
0ba50f4
Document the service worker operations
adamziel Nov 20, 2025
ae24313
Inject iframe trap from the service worker
adamziel Nov 20, 2025
ffb1b19
Clear iframes-trap.js
adamziel Nov 20, 2025
4acc659
Explore wrapping contentWindow and contentDocument
adamziel Nov 21, 2025
32f9f50
A passing set of fast tests
Dec 1, 2025
245245c
Get the iframes tram to work with a nested document
Dec 1, 2025
c33f7eb
Support arbitrary nesting of iframes
Dec 2, 2025
9cf08b2
Add TinyMCE test case
Dec 2, 2025
1214760
Theiretically fixed CI test failures
Dec 2, 2025
4911d3e
Adjust ci tests
Dec 2, 2025
8127cb5
Fix flaky data URL test by improving content wait logic
Dec 2, 2025
765513e
Fix CI test failures by keeping loader URLs within SW scope
Dec 2, 2025
0f8d378
Fix data URL race condition in iframes-trap.js
Dec 2, 2025
78b1709
Skip deeply nested iframes test on Firefox
Dec 2, 2025
c7e43ec
Revert "Skip deeply nested iframes test on Firefox"
Dec 2, 2025
89a2684
Fix cross-realm iframe src setting for Firefox deeply nested iframes
Dec 2, 2025
e5cee4d
Use postMessage for cross-realm iframe creation in Firefox
Dec 2, 2025
6dd0206
Add timeout to service worker ready check in tests
Dec 2, 2025
b9b0fde
Mark deeply nested iframes test as .only() for faster CI verification
Dec 2, 2025
e0c8e4e
Remove .only() from deeply nested iframes test
Dec 2, 2025
0c5057e
Fix TinyMCE iframe control in Firefox by handling pre-existing iframes
Dec 3, 2025
aea3b4f
Intercept document.write() on iframe documents for Firefox TinyMCE fix
Dec 3, 2025
fa1a5f4
Use contentDocument proxy to intercept document.write() on iframe doc…
Dec 3, 2025
756f360
Also proxy contentWindow.document to intercept TinyMCE document.write()
Dec 3, 2025
19057eb
Fix declaration order: documentProxyCache must be defined before cont…
Dec 3, 2025
7e6d8ac
Fix TinyMCE document.write() iframe control in nested contexts
Dec 4, 2025
280e347
Add debug logging to deeply nested iframe test for Firefox CI
Dec 4, 2025
78958dd
Improve waitFor helpers to use __controlledIframe and add more debug …
Dec 4, 2025
40fbb08
Wait for SW controller before responding to iframe creation request
Dec 4, 2025
f17212e
Add console logging to help debug Firefox iframe creation
Dec 4, 2025
cba0343
Add more debug logging to findCapableAncestor
Dec 4, 2025
7581c8c
Improve waitForContent to wait for injected content, not just body ex…
Dec 4, 2025
7c8a977
Improve waitForContent to check for iframes-trap.js loaded marker
Dec 4, 2025
9aaeb6a
Also check for iframes-trap.js loaded in typing test waitForIframeReady
Dec 4, 2025
0e7cf75
Add __playground_loader_complete__ marker for reliable content readin…
Dec 4, 2025
15f5e61
Simplify typing test waitForIframeReady by removing loaderComplete re…
Dec 4, 2025
839397e
Add TinyMCE integration test with typing and media upload
Dec 4, 2025
502b403
Fix srcdoc iframe race condition for top-level context
Dec 4, 2025
92428c5
Fix stuck data-srcdoc-pending when iframe already controlled
Dec 4, 2025
552360d
Fix TinyMCE and document.write iframe control
Dec 4, 2025
b5097fd
Fix document.write iframe CSS/resource loading via live proxy
Dec 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,111 changes: 2,111 additions & 0 deletions packages/playground/remote/iframes-trap.js

Large diffs are not rendered by default.

93 changes: 93 additions & 0 deletions packages/playground/remote/remote.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,99 @@
<html>
<head>
<title>WordPress Playground</title>
<!--
Set up the message listener for cross-frame iframe creation requests.
This is needed BEFORE the service worker is registered, because nested iframes
(like TinyMCE inside WordPress) may need to create controlled iframes in this
ancestor window. The full iframes-trap.js will be loaded by the service worker
for other pages, but this listener must be available immediately.
-->
<script>
// Minimal message listener for iframe creation requests from child frames.
// This is a subset of iframes-trap.js that handles the postMessage protocol.
(function() {
// Native createElement reference
const nativeCreateElement = Document.prototype.createElement;
const nativeSetAttribute = Element.prototype.setAttribute;
const nativeIframeSrc = Object.getOwnPropertyDescriptor(HTMLIFrameElement.prototype, 'src');

// Mark that we have the listener
window.__controlled_iframes_loaded__ = true;

// Storage for created iframes that children can access
window.__pg_iframes = {};

window.addEventListener('message', async (event) => {
if (event.data?.type !== '__playground_create_iframe') {
return;
}

const { config } = event.data;
const port = event.ports[0];

if (!port) {
return;
}

try {
// Create iframe using native createElement
const iframe = nativeCreateElement.call(document, 'iframe');
iframe.id = config.id;

// Copy attributes
if (config.attributes) {
for (const [name, value] of Object.entries(config.attributes)) {
iframe.setAttribute(name, value);
}
}

// Apply styles
if (config.style) {
Object.assign(iframe.style, config.style);
}

// Append to body
document.body.appendChild(iframe);

// Set src using native setter
if (config.src) {
if (nativeIframeSrc?.set) {
nativeIframeSrc.set.call(iframe, config.src);
} else {
nativeSetAttribute.call(iframe, 'src', config.src);
}
}

// Store reference so child can access it
window.__pg_iframes[config.id] = iframe;

// Wait for the iframe to have a service worker controller before responding.
// This is critical for Firefox where timing can be different.
const waitForController = async (maxWait = 10000) => {
const start = Date.now();
while (Date.now() - start < maxWait) {
try {
if (iframe.contentWindow?.navigator?.serviceWorker?.controller) {
return true;
}
} catch {
// Cross-origin or not ready
}
await new Promise(r => setTimeout(r, 50));
}
return false;
};

// Wait for controller, but don't block indefinitely
await waitForController();

port.postMessage({ success: true, iframeId: config.id });
} catch (error) {
port.postMessage({ error: error.message });
}
});
})();
</script>
<style>
* {
box-sizing: border-box;
Expand Down
Loading
Loading