Native-messaging API--catching a connection request failure versus a genuine disconnection

Is it possible to catch a connection error when attempting to connect to a native application?

nativeApp.port = browser.runtime.connectNative( "native_app_name" );

The above doesn’t return a promise but a port. I tried testing the port.error property for null, which it is null if a port is returned; but it is also null immediately after if a bad app name is provided. The error doesn’t appear to be generated until sometime later and the onDisconnect event is fired.

If write the port object to the console right after the connection request, in collapsed form, the error property displays null; but, when expanded, it displays an error object.

If write the value of port.error to the console, it displays null.

Am I doing something wrong? Why does onDisconnect fire when it could not connect, since the app name provided was intentionally wrong and doesn’t exist?

Is there a way to distinguish between a failure to connect and a dropped connection?

Thank you.

I think the port.onDisconnect.addListener could be used to detect connection issues - the even handler will contain information about error, see the docs:
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/Port#Type

port.onDisconnect.addListener((p) => {
  if (p.error) {
    console.log(`Disconnected due to an error: ${p.error.message}`);
  }
});

Thank you. I’ve been using the onDisconnect listener and it does provide an error message that appears to be the error property of the port object. I was asking about the timing between the connect request and the onDisconnect event. The connect request doesn’t appear to be synchronous but doesn’t return a promise either; so, you wait for a disconnect error, even though there was never a connection from which to disconnect, and check the text string for the error.

Perhaps a dropped connection can be simulated by ending the native app process in task manager to see what the error message might be in that case.

Somewhere along the line an error will be caught that the extension can’t communicate with the native application. I just wanted to know if there was something more specific than a text string, such as a code or returning NULL rather than a port object when a connection could not be established. But the onDisconnect event will suffice; it’ll have to.

Well, maybe someone else can respond here because this is a bit outside my expertise.
But if look at the runtime.connect” docs, it says for return value:

runtime.Port . Port through which messages can be sent and received. The port’s onDisconnect event is fired if the extension does not exist.

Anyway, the return value for both connect and connectNative is a Port object (not a promise) so because of the “single-thread” nature of JavaScript, nothing else can run in the meantime so if you register onDisconnect right away on the Port object, it must fire if the connection fails - because the connection doesn’t exists yet - at least I can’t imagine it could without a Promise involved - all IO operations are always non-blocking to prevent freezing.

Thanks. I’m not trying to complain about it, if it comes across like that.

Regardless of whether or not the request for a port was successful, nativeApp.port.error is null immediately after the request. The onDisconnect event does fire and at that stage the value is No such native application NatAppName.

nativeApp.port = browser.runtime.connectNative( "NatAppName" );
console.log( nativeApp.port );
// This won't work.
if ( nativeApp.port.error )
    {
      console.log( "Error connecting to nativeApp." );
      return;
    }
else
    console.log( "Communication port established." );

nativeApp.port.onDisconnect.addListener( ( p ) =>
   {
     console.log( p );
      if ( p.error )
         console.log( `Disconnected due to an error: ${ p.error.message }` );
   } );

It’s like browser.runtime.connectNative() is an asynchronous request that only tells you when it fails and not when it succeeded?

I don’t know how else it could be done, except perhaps like an indexedDB request has the result property in the successful request object, such that the port could be assigned after the connection is known to be successful.

It works as it is; I just wanted to make sure I wasn’t missing something. Thanks again.

1 Like

You are right:
runtime.Port objects do not have an “onConnectionSuccessful” event.

There are only two events you can listen for:

  • onDisconnect
  • onMessage

So you have to make do with them.

  1. Connect to the native app
  2. Send a test message to the native app
  3. Listen for both events

If the connection attempt was successful, the onMessage handler is called.
If not, the onDisconnect handler is called.

I’ve attached a test extension and native app.
The python file is zipped because this forum only lets you attach:
jpg, jpeg, png, gif, pdf, ics, zip

testconnectionbasedmessaging.zip (4.5 KB)
testconnectionbasedmessaging.py.zip (790 Bytes)

1 Like

Thank you for taking the time to put this together.

I believe I understand what your stating here and have had a similar set up.

The onDisconnect event will fire if the connection attempt fails regardless of whether an attempt to post a message to the native application is made. If that was ignored, the attempt to post a message to the native app would fail also, but it returns nothing but undefined whether successful or not.

I wanted to distinguish between a failed attempt to connect and a dropped connection; so, the text message of the error object appears to be the only way to do that, apart from maybe the columnNumber, fileName, lineNumber properties. If the process conhost.exe is ended via the task manager, assuming that closes the native app, the onDisconnect event doesn’t fire. I need to test what takes place when the native application closes due to programmatic exit or error, and when the extension closes the connection because it received an invalid JSON string. Perhaps, one of the those three properties makes a distinction. I’m pretty sure an invalid string fires the onDisconnect event.

I was considering providing a message that states, failed to connect, native app error/exited, extension closed the app–please click to try to reconnect. But that distinction is not necessary.

If the onDisconnect doesn’t fire for all of these cases, then subsequent attempts to communicate with the native application will fail silently and the extension will appear unresponsive. So, it’s a bit more important than just wanting a descriptive error message. But, as I wrote, I’ve a lot more to test and I need more on the native app side to do so properly.

I wondered about when a ping to the native app should be made to confirm that a connection has been established, since it appeared to be asynchronous in regards to being able to test port.error immediately after making the request to open a port. But you can post a message on the port immediately after making the request and it works.

nativeApp.port = browser.runtime.connectNative( "AppName" );
console.log( nativeApp.port.postMessage( "hello" ) ); 

It returns undefined but it works. Why does it work but the error message is delayed? Perhaps it is queued like writing the indexedDB event handlers after making the database request. I don’t know.

Thanks again.

I want to do something that is either very simple, or impossible.

I wrote a translation program in Python. It reads text or HTML from stdin until EOF, processes it for a couple of seconds, and writes the translated version to stdout. It works well.

I want to write a Firefox extension that passes every webpage to this program, in one piece so as not to break sentences, and displays the translated version.

I successfully installed emoji-substitution and native-messaging, but I need a raw HTML file, not a parsed DOM tree. Once I get it, I need to pass it to the native app, wait for the translation, and display it.

Are you asking how to get the raw HTML file? Perhaps something like this to convert the HTML DOM tree to a string for your translator to modify and then pass back to the DOM?

https://developer.mozilla.org/en-US/docs/Web/API/XMLSerializer/serializeToString

And these methods to add it back.

https://developer.mozilla.org/en-US/docs/Web/API/XMLSerializer#Inserting_nodes_into_a_DOM_based_on_XML