microsoft / vscode

Visual Studio Code
https://code.visualstudio.com
MIT License
163.17k stars 28.84k forks source link

Consider exposing WebUSB functionality in extensions #116761

Closed thegecko closed 2 years ago

thegecko commented 3 years ago

I'd be interested to understand whether there is appetite to add control of enabling WebUSB access in web extensions and webviews:

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Feature-Policy/usb

With VSCode going online through projects such as codespaces, there are strong use cases for accessing broader Web APIs and in this case, the ability to interact with devices attached to the user's machine. This could enable scenarios such as debugging of connected devices, which is already possible in the desktop version of VSCode.

I'd be happy to implement the feature, but would be keen to understand whether it would be accepted.

mjbvz commented 3 years ago

We'd consider this policies on a case by case basis

thegecko commented 3 years ago

We'd consider this policies on a case by case basis

Thanks, I've reworded the title and description of this request to focus on interest in exposing WebUSB in webviews.

mjbvz commented 3 years ago

Have you tested to see if this would work on the web? Webview contents are run inside iframes from a different origin than the main editor page. Even if we enable the policy on the iframe itself, I don't know that webUSB would work

You can try this using a local build of VS Code, modifying the iframe to have the correct permissions, and then running yarn web to start a browser with a test workspace

thegecko commented 3 years ago

Have you tested to see if this would work on the web? ...You can try this using a local build of VS Code

Many thanks for the pointers, @mjbvz. I have tested this change locally as you describe and can confirm that updating the allow permissions as follows:

element.setAttribute('allow', 'clipboard-read; clipboard-write; usb;');

Successfully enables webviews to invoke WebUSB features:

Screenshot 2021-02-19 at 13 40 55

I assume the feature to be implemented would be to control this setting from the webviewView.webview.options object?

thegecko commented 3 years ago

Perhaps WebHID and WebSerial should also be considered?

0Grit commented 3 years ago

Yes please. WebHID, WebSerial would be excellent additions.

These are the final pieces of the puzzle to open up embedded development to the masses.

akosyakov commented 2 years ago

It seems you can achieve it via web extensions, see https://github.com/gitpod-io/gitpod/issues/5814#issuecomment-937526878

0Grit commented 2 years ago

It seems you can achieve it via web extensions, see gitpod-io/gitpod#5814 (comment)

Not quite?

https://github.com/gitpod-io/gitpod/issues/5814#issuecomment-938401052

mjbvz commented 2 years ago

151795 introduces an experimental command (workbench.experimental.requestUsbDevice) that you can use on web to request USB access. This lets your extension code use the webUSB apis directly:

Here's how it works:

  1. To get permissions to access a usb device ( this is usb.requestDevice()) your extension must call this command instead of trying to call navigator.usb.requestDevice directly

    This is needed because your extension is running in a worker thread which cannot request permissions itself

  2. After the user selects some devices and finished the permissions flow, your extension code can then call navigator.usb.getDevices to access the selected devices

This command is only enabled on web and only works in Chrome/Edge. Please give it a try and let us know if you have any feedback


The new command relies on Chrome's permission delegation feature. While implementing it, I also realized that permission delegation makes enabling webUSB inside of our webviews too risky without a lot more thought.

The root problem is that once one iframe (webview) on a page request usb access, all the other usb enable iframes embedded into that page can also access that device without showing any prompts. It also means that if our top level page gets access to a usb device, all usb enabled iframes on the page can then also access that device. Instead we want all webviews to have to request permissions individually and then only be able to access their own pool of devices

I've opened a chromium issue about this behavior

thegecko commented 2 years ago

Great news we have some progress on this!

I'll try it when I find some spare cycles, but have some initial questions:

thegecko commented 2 years ago

Both of my concerns have been addressed in https://github.com/microsoft/vscode/pull/152257 👍

thegecko commented 2 years ago

FYI, PRs now open to expose similar functionality for WebSerial and WebHID using the same pattern:

thegecko commented 2 years ago

Experimental support has now been added, I believe this issue can be closed.

connor4312 commented 2 years ago

I tried this on insiders.vscode.dev using the webview-sample as a starting point

First, I added the following to the webview:

setInterval(() => {
    navigator.usb.getDevices().then(devices => {
        devices.forEach(device => {
            vscode.postMessage({
                command: 'alert',
                text: `Got product: ${device.productName}`
            });
        });
    })
}, 10_000);

Then I made the "do refactor" command request device access:

context.subscriptions.push(
    vscode.commands.registerCommand('catCoding.doRefactor', () => {
        vscode.commands.executeCommand('workbench.experimental.requestUsbDevice');
    })
);

While I saw the permissions prompt and granted access to my iphone, the request from the webview continued to error:

image

Are there steps I'm missing?

thegecko commented 2 years ago

The Web-* interfaces are only enabled in the extensionHost web worker, so I wouldn't expect the navigator access to work directly in webviews.

You should be able to access devices in the normal extension code, I'll try to find some time to create a demo.

thegecko commented 2 years ago

BTW @connor4312 how are you testing on insiders.vscode.dev? it seems the recommended approach is not currently possible due to the localtunnel server being unavailable.

thegecko commented 2 years ago

Please see this repo for a demo web extension which can request device authorisation through a webview and also list authorised devices:

https://github.com/thegecko/webusb-web-extension

connor4312 commented 2 years ago

The only reason localtunnel.me was used was to provide the extension over https, which I do locally with the serve CLI and a self-signed cert I made with mkcert.