Skip to content

Support copying non-pngs and wait for focus to avoid race conditions#180322

Merged
mjbvz merged 4 commits intomicrosoft:mainfrom
a-stewart:copyimage-fixes
Apr 20, 2023
Merged

Support copying non-pngs and wait for focus to avoid race conditions#180322
mjbvz merged 4 commits intomicrosoft:mainfrom
a-stewart:copyimage-fixes

Conversation

@a-stewart
Copy link
Contributor

This is a follow up to #180269 which fixes #171616

I patched the first PR to test it out and notices a couple of problems.

Firstly, when right clicking, it would often fail with the error message:

DOMException: Document is not focused.

This was because it was called by doing:

this.webviewEditor.reveal();
this.webviewEditor.webview.postMessage({ type: 'copyImage' });

Since .reveal() runs async, there was a race condition where the object was receiving focus and trying to copy at the same time, and for me, the copying was often winning.

I tried to fix this by adding an event listener on document to wait for it to gain focus, but this would also usually fail as it would usually gain focus whilst the listener was being setup, causing it to miss the focus gain event. Instead, I have a slightly less optimal solution which just waits 10ms and then checks again for focus. In testing this was never called more than once so I think that it is acceptable. Another solution would be to have a message sent back from the webview when it has focus, and wait for that in the copy command on the extension side, but I think this would be a lot more expensive.


The other issue I came across was that the copy only works for png images, other types it complains about the mime type not matching. I tried setting the mimetype based on the content type returned in the fetch, but it turns out the clipboard only supports png.

What I have done here is created a temporary canvas, which we can write the image into, avoiding the fetch() entirely. This allows us to get a png from any image type - when testing with gifs, this only keeps a single frame of the gif, so there is room for improvement there.

I think there is room for improvement here, but this resolves some of the issues I was seeing when copying images.

@a-stewart
Copy link
Contributor Author

@mjbvz

Copy link
Collaborator

@mjbvz mjbvz left a comment

Choose a reason for hiding this comment

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

Thanks for looking into this! Left some small feedback

canvas.getContext('2d').drawImage(image, 0, 0);
canvas.toBlob(async (blob) => {
try {
await navigator.clipboard.write([new ClipboardItem({ 'image/png': blob })]);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should we move the canvas drawing and toBlob call to be a promise inside ClipboardItem value?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added that, will test now to verify it works as expected.

// copyImage is called at the same time as webview.reveal, which means this function is running whilst the webview is gaining focus.
// Since navigator.clipboard.write requires the document to be focused, we need to wait for focus.
// We cannot use a listener, as there is a high chance the focus is gained during the setup of the listener resulting in us missing it.
setTimeout(copyImage, 10);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's limit the number of retries on this. If the document doesn't get focused for some reason, I don't want this to be looping forever

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've added a basic mechanism to limit it to 3 retries

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Increased to 5 retries 20ms apart. From testing 3 at 10 was regularly not enough, but 3 at 20 seems fine. Increasing to 5 retries at 20ms to be safe.

@mjbvz mjbvz enabled auto-merge (squash) April 20, 2023 18:44
@mjbvz mjbvz added this to the April 2023 milestone Apr 20, 2023
@mjbvz mjbvz merged commit 92d528b into microsoft:main Apr 20, 2023
@github-actions github-actions bot locked and limited conversation to collaborators Jun 4, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Copy does not work with image in media-preview extension

3 participants