Skip to content
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

Playground not loading on Android and Chrome #564

Closed
Tracked by #653
sumitsinghwp opened this issue Jun 18, 2023 · 13 comments
Closed
Tracked by #653

Playground not loading on Android and Chrome #564

sumitsinghwp opened this issue Jun 18, 2023 · 13 comments
Labels
[Aspect] Browser [Priority] High [Type] Bug An existing feature does not function as intended

Comments

@sumitsinghwp
Copy link

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
@eliot-akira
Copy link
Collaborator

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

@sumitsinghwp
Copy link
Author

I am using Google Chrome @eliot-akira

@dmsnell
Copy link
Member

dmsnell commented Jun 18, 2023

@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.

@adamziel
Copy link
Collaborator

@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)

@adamziel adamziel added [Type] Bug An existing feature does not function as intended [Aspect] Browser labels Jun 18, 2023
@adamziel
Copy link
Collaborator

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

@adamziel
Copy link
Collaborator

adamziel commented Jun 19, 2023

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.

@dmsnell
Copy link
Member

dmsnell commented Jun 19, 2023

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

@adamziel
Copy link
Collaborator

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

adamziel added a commit that referenced this issue Jun 22, 2023
Improve the UX at #564 by bubbling
up errors encountered when initializing workers.

This PR is meant to display an error message whenever there's a problem
downloading any of the assets required to run the Playground including:

* .wasm files
* .data files
* Dynamic JavaScript imports
* Web worker
* Service worker

Co-authored-by: Dennis Snell <dennis.snell@automattic.com>
Pookie717 added a commit to Pookie717/wordpress-playground that referenced this issue Oct 1, 2023
Improve the UX at WordPress/wordpress-playground#564 by bubbling
up errors encountered when initializing workers.

This PR is meant to display an error message whenever there's a problem
downloading any of the assets required to run the Playground including:

* .wasm files
* .data files
* Dynamic JavaScript imports
* Web worker
* Service worker

Co-authored-by: Dennis Snell <dennis.snell@automattic.com>
@adamziel
Copy link
Collaborator

adamziel commented Oct 4, 2023

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

@adamziel adamziel mentioned this issue Oct 4, 2023
10 tasks
@adamziel adamziel changed the title Mobile devices in not working properly Playground not loading on Android and Chrome Oct 13, 2023
@adamziel
Copy link
Collaborator

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

@adamziel
Copy link
Collaborator

adamziel commented Jan 2, 2024

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

@adamziel
Copy link
Collaborator

adamziel commented Jan 8, 2024

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.

adamziel added a commit that referenced this issue Jan 17, 2024
Makes the WordPress data module reject the loading promise on XHR error.
Before this commit, the promise would erroneously resolve even if the
request failed.

Related to #564
adamziel added a commit that referenced this issue Jan 17, 2024
Makes the WordPress data module reject the loading promise on XHR error.
Before this commit, the promise would erroneously resolve even if the
request failed.

Related to #564

## Testing Instructions

Rebuild the WordPress data module as follows:

```bash
node packages/playground/wordpress/build/build.js --wp-version=latest --output-js=packages/playground/wordpress/src/wordpress --output-assets=packages/playground/wordpress/public
```

Then make the following update to the `wp-6.4.js` file:

```js
// import dependencyFilename from './wp-6.4.data?url';
const dependencyFilename = 'http://localhost:3000/assets/wp-6.4-1d0e58cc.data';
export { dependencyFilename };
```

Then create a new file called `server.js` with the following contents:

```js
// Serve static assets from dist/packages/playground/wasm-wordpress-net/, but
// break the connection when the file is 90% downloaded

const express = require('express');
const app = express();
const path = require('path');
const fs = require('fs');

const port = 3000;

app.get('/*', (req, res) => {
	const filePath = path.join(
		__dirname,
		'dist/packages/playground/wasm-wordpress-net/' + (req.query.path || 'index.html')
	);
	const stat = fs.statSync(filePath);
	const fileSize = stat.size;

	const head = {
		'Content-Length': fileSize,
        'Content-Type': 'application/wasm',
        'Access-Control-Allow-Origin': '*',
	};
	res.writeHead(200, head);
	let sentSoFar = 0;
	const stream = fs.createReadStream(filePath);
	stream.on('data', (chunk) => {
		if (sentSoFar + chunk.length > fileSize * 0.9) {
			stream.destroy();
			res.end();
			return;
		}
        res.write(chunk);
		sentSoFar += chunk.length;
	});
});

app.listen(port, () => console.log(`Example app listening on port ${port}!`));
```

Then run `node server.js` and `npm run dev` and go to
http://localhost:5400/website-server/

You'll land in a version of Playground where only the first ~90% of the
WordPress data module is downloaded and then the connection is cut. You
should see the "oops, WordPress Playground had a hiccup" error message.
adamziel added a commit that referenced this issue Jan 29, 2024
Playground used the Emscripten's filepacker.py tool to bundle WordPress
releases as zip files. This caused a lot of problems:

* Filepacker splits WordPress into a `.data` file, with all the bytes
concatenated as a single blob, and a `.js` file with the map of offsets
and lengths for each file. Sometimes the browsers would invalidate the
cache for the `.data` file, but not the `.js` file, causing the
playground to fail to load.
* The `.js` files had to be patched in the Dockerfile.
* The `.js` files used XHR to load the `.data` files, which required
custom error handling logic, progress monitoring logic, and even then
some of the errors were not handled properly – see #564.
* The `.data` loading code required custom loading logic.
* The `.data` files were not stream–loaded. This PR doesn't stream-load
the `.zip` files either, but that feature may be added in the future
using the new `@wp-playground/compression-streams` package.

## Changes

* The WordPress build process now produces `.zip` files instead of
`.data`
  + `.js` files.
* The worker thread now loads WordPress from zip files, including zips
from arbitrary URLs.
* Query API and Blueprints now accept URLs via the `?wp=` parameter.

## Testing instructions

* Confirm all the E2E tests pass

cc @bgrgicak
@adamziel
Copy link
Collaborator

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Aspect] Browser [Priority] High [Type] Bug An existing feature does not function as intended
Projects
None yet
Development

No branches or pull requests

4 participants