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 support for wasm32 + web #160

Open
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

DouglasDwyer
Copy link

This PR implements the ability to cut, copy, and paste text on the web/WASM platforms. This allows libraries like egui-winit to use the clipboard on the web (see emilk/egui#4270). This is accomplished in the following way:

  • Setting or clearing the clipboard is accomplished by calling window.navigator.clipboard.write()
  • Reading from the clipboard is accomplished by adding a paste event listener to the root HTML document, which stores the event.clipboardData.getData("text") value into a global variable. Whenever the clipboard contents are requested from Rust, the global variable is read. Note that this means the user must actually perform a paste before the clipboard contents become visible to Rust. This is a caveat, but it's the only way that I could find to implement synchronous pasting.
  • Image copying/pasting is not implemented. When images are pasted in a browser, they are returned in the event.clipboardData.files array, where async is required to read the data.

Copy link
Collaborator

@complexspaces complexspaces left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the PR. I think the approach here makes sense for typical web applications. I'm not a web developer though 😅.

Question: Do you have the ability to test this in Safari? I know their Clipboard APIs are a bit different compared to Firefox and Chromium. I have the ability to test it myself but would need an example WASM app to run.

src/platform/wasm.rs Outdated Show resolved Hide resolved
const GLOBAL_CLIPBOARD_OBJECT: &str = "__arboard_global_clipboard";

pub(crate) fn new() -> Result<Self, Error> {
let window = web_sys::window().ok_or(Error::ClipboardNotSupported)?;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: Can this approach work in service workers/background workers? If not we can keep this hardcoded to window instead of global but that should be documented in the Clipboard documentation or similar.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately web workers cannot be supported - the clipboard API is not available in web workers (see the note on this page). I will add some documentation to mention this.

src/platform/wasm.rs Outdated Show resolved Hide resolved
src/platform/wasm.rs Outdated Show resolved Hide resolved
}

pub(crate) fn clear(self) -> Result<(), Error> {
let _ = self.clipboard.inner.write(&js_sys::Array::default());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the various clipboard calls failing return Error::Unknown?

Copy link
Author

@DouglasDwyer DouglasDwyer Aug 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The calls with let _ = don't fail immediately; they return a Promise that resolves once the browser has updated the clipboard contents. Since this is a synchronous API, I just fire and forget the promise here.

The only instances in which these calls asynchronously fail (according to Mozilla docs) is if the page doesn't have permission to write to the user's clipboard. Clipboard permissions are granted with transient activation - that is, once the user interacts with the page, writing to the clipboard is allowed.

It would be nice if there was a way to report the error here. The fire and forget approach is a bit jank - but it does work for my use case, which is just handling normal copy/paste interactions with egui on the web.

src/platform/wasm.rs Outdated Show resolved Hide resolved
@DouglasDwyer
Copy link
Author

I've gone ahead and attempted to address your feedback! The web has some security restrictions on what's possible, but this PR works perfectly for me with egui on the web, so I appreciate your review.

Question: Do you have the ability to test this in Safari? I know their Clipboard APIs are a bit different compared to Firefox and Chromium. I have the ability to test it myself but would need an example WASM app to run.

I sadly don't have access to a Mac right now. However, I've put together a really simple example web app for testing. You should be able to just clone the repository and run trunk serve to test it.

@complexspaces
Copy link
Collaborator

Hi, just a quick update: I don't have the bandwidth at the moment to finish up the testing and reviewing for this PR. I apologize for the late communication but I'll need to circle back to it later, likely in October.

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

Successfully merging this pull request may close these issues.

2 participants