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

Add a progress bar #46

Merged
merged 1 commit into from
Oct 15, 2022
Merged

Add a progress bar #46

merged 1 commit into from
Oct 15, 2022

Conversation

adamziel
Copy link
Collaborator

What problem does this PR solve?

Introduces a progress bar.

Loading WASM WordPress requires downloading about 20MB of files. Waiting without any feedback is a bad experience, not to mention you can never be sure whether it crashed or is still loading.

Solves #36

Screencast

There are two loading stages:

  1. The progress indicator bounces until we start loading the WASM files
  2. The progress indicator morphs into a progress bar and grows to reflect the data transfer
CleanShot.2022-10-14.at.17.31.41.mp4

How does this PR solve it?

This commit introduces a progress bar. The idea is to capture the progress events in wasm-worker.js, pass them on to the main thread, and then use the information to update a CSS progress bar.

We can’t simply update the WASM loading code to capture the progress events because that codeis generated by emscripten on each build. Instead, this commit introduces aWASMDownloadMonitor class that:

  • Monkeypatches WebAssembly.instantiateStreaming to capture the php-wasm.wasm progress events
  • Observes emscripten’s dataFileDownloads using ES Proxies to capture the wp.data progress events

It also introduces a new worker backend method called addMessageListener. It’s necessary to propagate the progress information from the worker to the main thread.

Finally, it reduces the init() method to a simple wrapper that can be configured as needed in the app. This allows the seamless wordpress.html to just load WordPress and be done, and the more involved wordpress-browser.html to handle the progress bar internally.

Loading WASM WordPress requires downloading about 20MB of files. Waiting without any feedback is a bad experience, not to mention you can never be sure whether it crashed or is still loading.

This commit introduces a progress bar. The idea is to capture the progress events in `wasm-worker.js`, pass them on to the main thread, and then use the information to update a CSS progress bar.

We can’t simply update the WASM loading code to capture the progress events because that codeis generated by emscripten on each build. Instead, this commit introduces a`WASMDownloadMonitor` class that:

* Monkeypatches WebAssembly.instantiateStreaming to capture the `php-wasm.wasm` progress events
* Observes emscripten’s `dataFileDownloads` using ES Proxies to capture the `wp.data` progress events

It also introduces a new worker backend method called `addMessageListener`. It’s necessary to propagate the progress information from the worker to the main thread.

Finally, it reduces the `init()` method to a simple wrapper that can be configured as needed in the app. This allows the seamless `wordpress.html` to just load WordPress and be done, and the more involved `wordpress-browser.html` to handle the progress bar internally.
@adamziel adamziel merged commit 808f967 into trunk Oct 15, 2022
@adamziel adamziel deleted the add/progress-bar branch October 15, 2022 03:34
@adamziel
Copy link
Collaborator Author

Actually this needs a follow-up. I just realized that it works nicely in my local setup because the dev server sends the Content-length header and we know the total bytes to download. In production, however, the file is sent over the wire gzipped and we don't know the content-length upfront. I'm thinking about baking in a content-length into the JavaScript bundle during the build process.

@adamziel
Copy link
Collaborator Author

Precomputed content lengths added in b21c76b

eruizdechavez pushed a commit to eruizdechavez/wordpress-playground that referenced this pull request Sep 20, 2023
@ivancuric
Copy link

Hey! Just wanted to ask how is this different from simply hooking into Module.setStatus?

I'm using the following snippet to convert the returned text into a numerical progress:

export function convertEmscriptenStatusToProgress(emStatus: string): number {
  // roughly based on https://github.com/emscripten-core/emscripten/blob/1.39.11/src/shell.html#L1259
  if (emStatus === "Running...") {
    // download has completed, wasm execution has started
    return 100;
  } else if (emStatus.length === 0) {
    // empty message
    return 0;
  }

  const regExp = RegExp(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
  const match = regExp.exec(emStatus);
  if (match) {
    const currentValue = parseInt(match[2]);
    const maxValue = parseInt(match[4]);
    return (currentValue * 100) / maxValue;
  } else {
    // Cannot parse emscripten status
    return NaN;
  }
}

@adamziel
Copy link
Collaborator Author

These days we hook into fetch() response streaming and also use the same message exchanging utilities to monitor the progress of downloading WordPress zip and other files. I didn't know about Module.setStatus() – thank you for sharing! I'd be concerned about the format of that string changing without a warning one day, though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants