WordPress/wordpress-playground

Missing CSS and images because the Gutenberg iframe isn't controlled by the service worker

Lovor01 opened this issue · 13 comments

This happens only in https://playground.wordpress.net/, when run locally as VS code extension, the issue does not appear.

Block inserter "+" icon is not styled; some other UI elements (like buttons when inserting new image block, buttons on starter template on columns block, etc) are not styled as well; when inserting new image and uploading it through interface, the image does not show in block editor.

Tested on Firefox developer version 118 b9 and Chrome dev version 118 on Windows 10.

Steps to reproduce:
Start new WP instance in playground. Create new post, insert image from local file.

Related

Same here (latest MacOS Firefox and Chrome). Anyone know how to fix this?

Network inspector shows a bunch of 404s for URLs like https://playground.wordpress.net/scope:0.5858414608182674/wp-includes/css/dist/block-editor/style.min.css?ver=6.3 which originate from an iframe rendered by Gutenberg. That iframe has src="blob:https://playground.wordpress.net/9c82f60a-1f1e-4abe-adde-6b5b62f51043" and I'm almost certain it's considered cross-origin and thus the requests it issues are not handled by the service worker. It's this issue all over again:

#42

I'm not sure what the fix is yet.

Here's what I found:

  • The iframe has the same origin as the parent window
  • The iframe still isn't being controlled by the service worker
  • The iframe can't register its own service worker

Here's the script I used to verify the above:

async function init() {
    // Register a service worker
    await navigator.serviceWorker.register('/sw.js', { scope: '/' });
    console.log('Registration successful');
    // Reload the page after the first time this script runs

    // Now, confirm the top-level script is controlled by the service worker
    console.log("I'm an top-level script", {
        origin: window.location.origin,
        swController: navigator.serviceWorker.controller
    });

    // Let's create a blob-based iframe and check if it's controlled by the service worker
    const blob = new Blob([`
    <html>
        <body>
            <h1>Test iframe</h1>
            <script>
                console.log("I'm an iframe", {
                    origin: window.location.origin,
                    swController: navigator.serviceWorker.controller,
                    protocol: window.location.protocol
                });
            </script>
        </body>
    </html>
    `], { type: 'text/html' });
    const blobUrl = URL.createObjectURL(blob);
    const iframe = document.createElement('iframe');
    document.body.appendChild(iframe);
    iframe.src = blobUrl;

    // And sadly, the iframe is not controlled by the service worker :-(
}
init();

To sum it all up:

  • An iframe using src set to a binary blob isn't controlled by a service worker
  • An iframe using srcdoc had a null origin and also isn't controlled by a service worker
  • Ditto for using a data URL
  • An iframe loaded using a regular src="" URL worked well

URL-based src like src="/doc.html" seems to be the only way forward. Or am I missing something? cc @dmsnell

@ellatrix can you remember what was the rationale behind using a blob vs a URL? Was it saving an extra request and figuring out the right file path? Would it be viable to create an extension point to enable forcing src="file.html" in Playground?

no idea @adamziel, though I think the blob has memory implications for Gutenberg.

We switched to blob to fix the origin issue, it's also a bit cleaner that srcDoc. Why does it need to be controlled by the service worker? It's purely added in JS, nothing server side? Are we talking about the editor content iframe or something else? I thought everything was working fine?

@ellatrix the root problem was always the service worker not handling the requests from the editor content iframe – like loading style.css for specific blocks. When it does, we say the iframe is controlled by the service worker.

In our chat I somehow reached a wrong conclusion. I thought that the iframe only needs to have the same origin to be controlled. It doesn't. The blob-based editor content iframe now has the same origin as the top-level page, but it still isn't controlled. The only way I was able to make it controlled was by using a path-based src like src="/document.html"

We'd need to revert WordPress/gutenberg#50875 in Gutenberg, but then the original issue it solved would return:

The problem with srcDoc is that window.location is not filled, so relative hash links will trigger a navigation change and reload the page. It would also remove the need for a hack in WP playground.

For now I'll see if I can patch this in Playground to fix all the 404s, and then let's see what a proper solution would look like. cc @ellatrix

Edit: This is more difficult than just swapping src={blob} with src={url} because the initial document now loads all related CSS assets. Hm.

Edit 2: This worked for me locally:

In WordPress/Gutenberg:

src: '/wp-includes/empty.html?doc='+encodeURIComponent(renderToString( styleAssets ))

In empty.html:

<!doctype html>
<script>document.write(decodeURIComponent(window.location.search.substring(5)))</script>

However, this creates an XSS vulnerabilty. It's fine for Playground since you can run custom code anyway, but it wouldn't work for WordPress core at all. I do not see a single solution that would suit both WordPress core and Playground here and we may be stuck with patching that part of WordPress until there is one.

Encountered this as well during the brief period the Live Preview button was in the Plugin Directory. A bunch of 404s and unstyled content of the Block Editor.

Was using latest Arc on latest MacOS.

Would recommend making this urgent issue.

URL-based src like src="/doc.html" seems to be the only way forward. Or am I missing something? cc @dmsnell

It's using a blob URL, no? 😉

Sure it does @ellatrix. Let's call them HTTP URLs and blob URLs then :p

Short-term fix coming in #668
Long-term fix is being explored in the Gutenberg repo at WordPress/gutenberg#55152

#668 just landed and I'm about to deploy it to playground.wordpress.net. Let's keep this issue open until the upstream Gutenberg PR gets resolved in one way or another.