clap icon indicating copy to clipboard operation
clap copied to clipboard

Webview extension

Open geraintluff opened this issue 8 months ago • 2 comments

This draft lets CLAP plugins straightforwardly use a web-page for their UI.

Messages are exchanged in both directions as opaque blocks of bytes on the main thread. The webpage receives and sends messages using standard web APIs.

geraintluff avatar Apr 10 '25 15:04 geraintluff

One thing that I'm wondering is if the plugin needs to be able to specify a minimum requirement for html, css, javascript ? Or this is better done within the page that will be loaded?

abique avatar Jun 06 '25 21:06 abique

@abique I think the requirements-check is best done from inside the page. Websites manage this OK (outside of ancient enterprise workplace tools which don't work in Firefox or whatever 😄), and modern web tech is pretty stable.

geraintluff avatar Jun 07 '25 08:06 geraintluff

@abique For the starting URI length, can we somehow fix the capacity at CLAP_PATH_SIZE? 1kB seems long enough, gives more consistency from hosts, and avoids an extra method in the API.

geraintluff avatar Jun 25 '25 10:06 geraintluff

@abique For the starting URI length, can we somehow fix the capacity at CLAP_PATH_SIZE? That seems long enough, gives more consistency from hosts, and avoids an extra method in the API.

For the draft we can, for the final version I'd need to think more about it. Do the web browser have a max url size? We could also define a CLAP_URI_SIZE if we want more space.

abique avatar Jun 25 '25 11:06 abique

Ah you said that chrome has a max. Then we could go this way I suppose.

abique avatar Jun 25 '25 11:06 abique

I think it'd be good to set a fixed size, because what is a plugin meant to do if the host gives it a smaller capacity? The maximum length varies by browser/webview, but none of them are below 1024.

For context, this URL is 509 bytes - less than half the 1024 of CLAP_PATH_SIZE:

http://localhost:8000/tmp/web-audio/?url=/tmp/dynamap/web-audio/DynaMap.js&ui=/tmp/dynamap/web/#dynamap?preset=vVLBSsRADP2XnKNMlYr2qCx48aQ32UNsx93QdqY0abGU-XfZaWV3sUUQ9JSQeXnvJZMRetsKewfZDQI5qgZhgWw81q8QGtuyL55inXLl3kJmEHqqOgtZagICqVJe3nsvuowyl2lAqLwvaW9plWyGsSuX35MQEIrBUc35uc8E4W1oSNaIA0LetYfiSZNBaDw7FcheR_iA7OL6DmGYow5N7NweRLUlJ2wjdjxb20-yUnFuJ4XTtta-H7aQItRT2BFPc6h3E4nzLFNmC9ZnJf1mv-50QoivfEy-7JgQcFExmSWTf9RMzTym-QvNLYJ0opF3-WoQxDbUkka-RUy8vJnmJTpb-9Bo95F3-4du5dpvjTFHts2vhgqf&audio=audio%2Fexample.mp3

Local paths are relative to the plugin bundle, so length shouldn't be a problem, and an absolute URL of 1024 bytes is long enough to hold quite a lot of config/auth-tokens/SHA-sums/etc.

geraintluff avatar Jun 25 '25 11:06 geraintluff

The max URL size isn't set in stone in browser.

The approach I suggested is maybe a bit boring to impl and use, but it has the advantage that we don't have to think about it in the future and we won't be sorry in the future because it is constrained to a small size.

abique avatar Jun 25 '25 11:06 abique

OK - can the length query be a separate method (rather than calling stuff with NULL)?

// Expected length of the URI. For compatibility, this should not exceed 2000 bytes
uint32_t (CLAP_ABI *get_uri_length)(const clap_plugin_t *plugin);

// ... existing docs
// + "capacity must be at least that returned by get_uri_length()"
bool (CLAP_ABI *get_uri)(const clap_plugin_t *plugin, char *uri, uint32_t uri_capacity);

geraintluff avatar Jun 25 '25 11:06 geraintluff

If you have just one method

int32_t (CLAP_ABI *get_uri)(const clap_plugin_t *plugin, char *uri, uint32_t uri_capacity);

Which always returns the URI length, then you're good. If the returned value is <= 0, then you consider that there was an error and the uri couldn't be computed by the plugin.

You can call it with NULL, but event if you pass a buffer that is too big or too small you still get a null terminated string and can figure out if it is truncated by comparing the returned sized and your capacity.

In the host code base or any code base that would use this approach, you can have utility code that will compute a std::string out of this interface. If you have two functions: one for the length and one for the content, you'll have to provide two function pointers instead of one to the utility. And two function pointers means a mismatch risk like asking for the size of A while getting the content of B.

Vulkan is doing something similar to read dynamic size array and once you wire your utility code, it is easy to work with.

abique avatar Jun 25 '25 11:06 abique

So if the host provides a buffer that's too small, should the plugin return the size we wants the URI, not the actual size it's returning?

geraintluff avatar Jun 25 '25 11:06 geraintluff

So if the host provides a buffer that's too small, should the plugin return the size we wants the URI, not the actual size it's returning?

Yes. URI: 64 bytes get_uri(capacity: 24) -> returns 64.

abique avatar Jun 25 '25 11:06 abique

OK - and should uri be allowed to be null?

We could demand that it's always not null, but allow a capacity of 0 for a length query. This would match discussions about audio_outputs where the conclusion (as I understood it) was that the array cannot be a null pointer, even if audio_outputs_count == 0.

geraintluff avatar Jun 25 '25 11:06 geraintluff

std::string get_dyn_str(std::function<int32_t (char *, uint32_t)> f)
{
   const auto sz = f(nullptr, 0);
   std::string s;
   s.resize(sz);
   f(s.data(), sz);
   return s;
}

auto uri = get_dyn_str([&] (char *s, uint32_t c) -> int32_t { return webview->get_uri(plugin, s, c); });

This is more or less what I have in mind on how to use this. Once you wire it well, it is easy to work with.

abique avatar Jun 25 '25 11:06 abique

OK, got it! A null uri is unexpected enough (for me) that I've add a note in the comments, but otherwise the API should match what you were suggesting.

geraintluff avatar Jun 25 '25 11:06 geraintluff

OK - and should uri be allowed to be null?

I'd say that if uri is null then capacity must be 0. And if capacity is 0, then uri can't be used (so its value doesn't matter).

We could demand that it's always not null, but allow a capacity of 0 for a length query. This would match discussions about audio_outputs where the conclusion (as I understood it) was that the array cannot be a null pointer, even if audio_outputs_count == 0.

If you're talking about https://github.com/free-audio/clap/blob/main/include/clap/process.h#L51 Then if the port count is 0, then it doesn't make a difference if the pointer is null or not because you can't use it.

abique avatar Jun 25 '25 12:06 abique

Oh sorry, I've forgotten to merge!

abique avatar Jul 16 '25 14:07 abique

@abique I've added a comment saying that if get_uri() returns a file: URI, the host is allowed to rewrite the URL to some other scheme/prefix, as long as the filename and all relative paths are preserved.

This has two functions: in a native environment, file: URLs have some limitations about the requests they can make. Additionally, this lets non-bundled WCLAPs write their resources to some directory (using the WASI virtual filesystem) and then point to it, the same way as platforms like Windows.

In the case of bundles (which aren't supported across all OSes), I've said that relative paths should be resolved against a file: URL for the bundle's resource directory, which I think is nice and clear.

geraintluff avatar Jul 16 '25 14:07 geraintluff

@abique I've added a comment saying that if get_uri() returns a file: URI, the host is allowed to rewrite the URL to some other scheme/prefix, as long as the filename and all relative paths are preserved.

This has two functions: in a native environment, file: URLs have some limitations about the requests they can make. Additionally, this lets _non-_bundled WCLAPs write their resources to some directory (using the WASI virtual filesystem) and then point to it, the same way as platforms like Windows.

In the case of bundles (which aren't supported across all OSes) relative paths should be resolved against a file: URL for the bundle's resource directory.

geraintluff avatar Jul 16 '25 14:07 geraintluff

Thank you very much! :-)

abique avatar Jul 16 '25 14:07 abique

This is awesome, thanks so much.

I don't know if you saw, but WCLAPs got a small shout-out in one of the ADC'25 talk proposals (about the future of audio plugins on the web).

geraintluff avatar Jul 16 '25 14:07 geraintluff

This is awesome, thanks so much.

I don't know if you saw, but WCLAPs got a small shout-out in one of the ADC'25 talk proposals (about the future of audio plugins on the web).

I didn't see it but that's great :-)

abique avatar Jul 17 '25 09:07 abique