Is connectNative blocking?

127 views
Skip to first unread message

Matt

unread,
Nov 3, 2024, 4:57:22 PMNov 3
to Chromium Extensions
I want to check I'm understanding what I've been looking at correctly, so I'd appreciate any help on this before I go any further.

Connecting to a native messaging host by calling connectNative is synchronous, so it blocks. This call is made in the service worker, and we can't create a new Worker within the service worker, so if it is slow it blocks the service worker. Even worse, when sending messages to the native messaging host, the service worker can't receive messages.

Is my understanding correct, and if so is there a way to fix this?


woxxom

unread,
Nov 4, 2024, 3:14:10 AMNov 4
to Chromium Extensions, Matt
It's not blocking. When you call an asynchronous `chrome` API (such as messaging), it just sends an internal message to the browser process, which then performs the actual work (connects to the native host) and sends the result back (the actual port handle in this case as opposed to the virtual one returned by chrome.runtime.connectNative in JS).

>  we can't create a new Worker within the service worker

This is unrelated and is caused by an intentional limitation of the service worker specification. Hopefully, it will be relaxed for extensions eventually, because this limitation doesn't make sense in case of extensions (similarly to the other limitations such as URL.createObjectURL) and forces us to use the clunky and wasteful chrome.offscreen API.

> when sending messages to the native messaging host, the service worker can't receive messages.

Sending a message starts with serializing the message into a string, which may take some time as Chrome is still using the super inefficient JSON stringification internally. A huge message may take many seconds to serialize. Since this is performed in the caller, it blocks the JS engine for the entire duration.

> is there a way to fix this?

Use a different connection mechanism such as WebSocket or WebTransport.
With the native messaging the only "solution" is to send smaller messages.

Matt

unread,
Nov 4, 2024, 5:07:09 AMNov 4
to Chromium Extensions, woxxom, Matt
Thanks woxxom, I appreciate you know your stuff so not questioning it but I did read elsewhere that connectNative was blocking, to save future questions asking about other method calls, where is this documented so that I can check for myself as I did look and couldn't see where it was stated to be async.

Being async, it won't block, which is great news, but any idea why can't we use the await keyword on it? If we could, we could have wrapped the call in a promise and raced the two so that if connectNative took a long time we could time it out. Are there any internal mechanisms in the chromium code to handle this scenario?

"Use a different connection mechanism such as WebSocket or WebTransport." I imagine that in doing so, the native messaging host would be started outside of the browser and it would effectively be a standalone app, I can see how this would be a lot easier to work with too. Are there any drawbacks to using WebSocket or WebTransport over using the native messaging host implementation that is provided by browser vendors?

woxxom

unread,
Nov 4, 2024, 5:23:52 AMNov 4
to Chromium Extensions, Matt, woxxom
The authoritative source is the source code: https://crsrc.org/extensions/renderer/api/runtime_hooks_delegate.cc;l=446;drc=5de3cdee and it says that the same mechanism is used as for any other `chrome` messaging: a `gin` port that uses the internal `mojo` messaging mechanism, fully asynchronous. There's also no explicit pausing of the JS interpreter.

> any idea why can't we use the await keyword on it

The result of this call is actually a virtual handle returned immediately in a matter of microseconds. This is how the API is implemented. Whoever did it, thought it was a good design.

>  we could have wrapped the call in a promise and raced the two so that if connectNative took a long time we could time it out. Are there any internal mechanisms in the chromium code to handle this scenario?

Since the virtual port is returned instantly, you can already do what you describe by immediately using port.postMessage('ping') and setTimeout, which retries the connection on no response within the preset time. You can also wrap it in a Promise.

> Are there any drawbacks to using WebSocket or WebTransport over using the native messaging host implementation that is provided by browser vendors?

I don't know, but generally the extensions API is inferior to the web platform API as the former has ~1000x less programmers working on it.

woxxom

unread,
Nov 4, 2024, 8:59:52 AMNov 4
to Chromium Extensions, woxxom, Matt
I guess the reason for the confusion may be the fact that the JS function chrome.runtime.connectNative itself is synchronous and thus indeed it blocks execution for the duration of its run, but that's a mere microsecond or a millisecond, because its result is just a virtual wrapper for the port, while the actual connection is performed asynchronously in the browser process. BTW, that's why to detect an error in that connection attempt we have to use port.onDisconnect.addListener(() => { if (chrome.runtime.lastError) console.log('error') });
Reply all
Reply to author
Forward
0 new messages