WordPress/wordpress-playground

Playground not loading on Android and Chrome

sumitsinghwp opened this issue ยท 13 comments

Hey,

I have tried to test playground in my mobile and it's loading countinue. I am using android OS.

Also when try to change php version in pop-up then still not able to change.

More information see record video.

Screenrecorder-2023-06-18-08-59-02-608.mp4

Could you tell us which browser you're using, in case it might be relevant?

I am using Google Chrome @eliot-akira

@sumitsinghwp I'm seeing the same thing, and I've let it load for near ten minutes. I don't have the console output, so I'm not sure what's going on. Any chance you can get a copy of the output logging and share? or anyone? I think one needs to connect to Android Studio or download some Dev Tools app to do this.

On Firefox on Android it's loading fine, so definitely not a network issue.

@ellatrix it looks like the Blocknotes issue you're experiencing too! Playground should be better about communication problems and at least say "network error, click here to retry" and output some useful info in devtools (if it doesn't already)

This is the code fragment at fault whenever the progress bar gets stuck at 50%:

// Connect the Comlink client and wait until the
// playground is ready.
const playground = consumeAPI<PlaygroundClient>(
iframe.contentWindow!
) as PlaygroundClient;
await playground.isConnected();
progressTracker.pipe(playground);
const downloadPHPandWP = progressTracker.stage();
await playground.onDownloadProgress(downloadPHPandWP.loadingListener);
await playground.isReady();
downloadPHPandWP.finish();
return playground;

I know that, because startPlaygroundWeb splits the progress bar in two halfs:

  • 50% tracks PHP and WordPress loading
  • 50% tracks Blueprint execution

This can be somewhat seen here:

const compiled = compileBlueprint(blueprint, {
progress: progressTracker.stage(0.5),
onStepCompleted: onBlueprintStepCompleted,
});
const playground = await doStartPlaygroundWeb(
iframe,
setQueryParams(remoteUrl, {
php: compiled.versions.php,
wp: compiled.versions.wp,
}),
progressTracker
);
await runBlueprintSteps(compiled, playground);

I believe this await statement finishes, but it would be great to use a timeout and report a problem when it doesn't:

 await playground.isReady();

I think I saw the progress bar slowly moving forward for a tiny bit, which would suggest the first Blueprint step attempts to run:

const run = async (playground: UniversalPHP) => {
try {
stepProgress.fillSlowly();
return await stepHandlers[step.step](
playground,
await resolveArguments(args),
{
tracker: stepProgress,
initialCaption: step.progress?.caption,
}
);
} finally {
stepProgress.finish();
}
};

But, again a guess, it throws an error and never finishes. It would be lovely to display these errors in the UI to at least tell the user what's going on.

As to why โ€“ I'm not sure yet. I've seen this happening when a cached remote.html is loaded and it attempts to download a non-existent php.wasm.

There is a slight chance that #561 solved this issue. We'll know for sure once new playground.wordpress.net is deployed โ€“ perhaps tomorrow

Well it can't be a Blueprint error because those would advance the progress bar to completion (whether that behavior makes sense is a separate discussion):

try {
// Start resolving resources early
for (const { resources } of compiled) {
for (const resource of resources) {
resource.setPlayground(playground);
if (resource.isAsync) {
resource.resolve();
}
}
}
for (const { run, step } of compiled) {
const result = await run(playground);
onStepCompleted(result, step);
}
try {
await (playground as any).goTo(
blueprint.landingPage || '/'
);
} catch (e) {
/*
* NodePHP exposes no goTo method.
* We can't use `goto` in playground here,
* because it may be a Comlink proxy object
* with no such method.
*/
}
} finally {
progress.finish();
}

It can't be await playground.isConnected(); either, because it would fail after a timeout and the progress bar is only set up after isConnected() resolves:

get: (target, prop) => {
if (prop === 'isConnected') {
return async () => {
/*
* If exposeAPI() is called after this function,
* the isConnected() call will hang forever. Let's
* retry it a few times.
*/
for (let i = 0; i < 10; i++) {
try {
await runWithTimeout(api.isConnected(), 200);
break;
} catch (e) {
// Timeout exceeded, try again
}
}
};
}
return (api as any)[prop];
},

Therefore, it's either playground.isReady() or something really surprising. That one wouldn't resolve if either:

The web worker doesn't report it's ready, and that would happen when it cannot load PHP or WordPress

const [setApiReady] = exposeAPI(
new PlaygroundWorkerEndpoint(php, monitor, scope, wpVersion, phpVersion)
);
await phpReady;
if (!useOpfs || !wordPressAvailableInOPFS) {
/**
* When WordPress is restored from OPFS, these patches are already applied.
* Thus, let's not apply them again.
*/
await wordPressModule;
applyWebWordPressPatches(php);
await applyWordPressPatches(php, {
wordpressPath: DOCROOT,
patchSecrets: true,
disableWpNewBlogNotification: true,
addPhpInfo: true,
disableSiteHealth: true,
});
}
if (useOpfs) {
if (wordPressAvailableInOPFS) {
await copyOpfsToMemfs(php, opfsDir!, DOCROOT);
} else {
await copyMemfsToOpfs(php, opfsDir!, DOCROOT);
}
journalMemfsToOpfs(php, opfsDir!, DOCROOT);
}
// Always setup the current site URL.
await applyWordPressPatches(php, {
wordpressPath: DOCROOT,
siteUrl: scopedSiteUrl,
});
setApiReady();

The service worker is not getting registered

const [setAPIReady, playground] = exposeAPI(webApi, workerApi);
await workerApi.isReady();
await registerServiceWorker(
workerApi,
await workerApi.scope,
serviceWorkerUrl + ''
);
wpFrame.src = await playground.pathToInternalUrl('/');
setupPostMessageRelay(wpFrame, getOrigin(await playground.absoluteUrl));
setAPIReady();

Either way, playground.isReady() should be updated to bubble up any errors to the user.

@adamziel this is Chrome-specific on Android. there's probably a clue in there.

I'm exploring better error reporting in #570, this should give us more answers

Does this work for you now by any chance, @sumitsinghwp? I wonder if any of the changes made in the meantime helped at all.

I'm closing this issue as I could not reproduce the problem on multiple Android browsers at browserstack. Feel free to comment if the problem pops up again @sumitsinghwp! Brownie points if you could provide the output from developer tools.

Here's what I saw in my testing:

Chrome on Pixel 7 (Android 12):

CleanShot 2023-10-13 at 13 15 16@2x

Chrome on Pixel 8 Pro (Android 13):

CleanShot 2023-10-13 at 13 14 22@2x

Galaxy S23 (Android 13):

CleanShot 2023-10-13 at 13 22 34@2x

Galaxy S22 Plus (Android 12):

CleanShot 2023-10-13 at 13 18 55@2x

Galaxy S21 (Android 11):

CleanShot 2023-10-13 at 13 20 10@2x

I just ran into this issue on desktop, it happened when the wp.data couldn't be loaded. I would expect an error to be thrown, but it just got stuck.

CleanShot 2024-01-02 at 14 37 33@2x

The problem here is specific to .data files produced by emscripten's filepacker. They use XHR and require separate error handling flows. Maybe we could just ditch .data entirely and just switch to shipping WordPress files through streamed .zip archives as enabled by #851.

This should be fixed by #978. Any WordPress loading errors will now be clearly surfaced.